目前,國內主流的前端應用框架具備兩個:vue.js和react.js,關於vue和react的優劣性,網上衆說紛紜。在下就不在此引戰。html
而是直接介紹React前端
🐋🐋🐋 vue和React這種都是快速應用開發工具,可能也會像曾經如日中天的JQuery被市場淘汰,因此我的建議不要盲目只追求快速工具的使用,而是花時間去學習原點。例如設計思想和數據結構。快速應用框架(或語言)只不過是應用工具而已。vue
🐋 之前都說是「三大框架」,還有一個Google開發的Angular,可是國內Angular使用份額愈來愈少。node
我的感受Angular主要問題是上手成本。Angular比較偏向於後端,不少概念對於前端開發人員都是噩夢。不過對於前端工程化,我的認爲Angular是集大成之做。我的建議,對於有經驗的朋友,能夠稍微學習下Angular中的思想。react
React是一個用於構建用戶界面的 JavaScript 庫,jquery
React自己是一個特別簡單的庫:將元素抽象爲虛擬DOM,更新DOM時對比虛擬DOM,而後只更新那些真正須要更新的元素。webpack
使用Document構建DOM時,都是使用 document.createElement() 來構建標籤git
const li = document.createElement('li'); document.body.appendChild(li)
在React中, 也提供了這樣一個自定義函數來React組件。github
React.createElement() 返回的是一個React自定義的元素類型:ReactElementweb
const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' );
React提供的React.createElement()和ReactElement提供了很好平臺隔離性。
使用同一套代碼編寫的元素組件只須要對接不一樣平臺的APi,就能夠實現跨平臺。
React可以跨平臺的緣由也在於此。
在平常開發中,也會常常寫無關業務的通用封裝,其思想與此相似。
在直接使用Document更新DOM元素時,不少時候會由於某些緣由 對沒必要更新DOM進行更新 從而產生了性能浪費
解決這個問題通常想到的作法就是作一個DOM緩存。建立DOM時將DOM信息緩存,更新時對比新舊DOM。排除掉沒必要要的更新DOM。
這種緩存DOM數據的方案就叫虛擬DOM(Virtual DOM), 而排除算法叫作diff算法
React也使用了這種方案提高性能
虛擬DOM(Virtual DOM)和diff算法 是對數據結構和算法的考驗。每個人均可以模擬出簡單的方案,但不是每個人均可以寫出優秀的解決方案。
在下愚鈍,對於數據結構和算法掌握的很差。因此對虛擬DOM(Virtual DOM)和diff算法只有淺薄的認知。有興趣的朋友能夠看一下這篇文章:深度剖析:如何實現一個 Virtual DOM 算法
React是經過JS構建元素的,
咱們都知道使用JS編寫頁面痛苦是沒有結構性。
使用HTML兩個標籤能搞定的事,使用JS就能寫一大堆代碼。
React爲了解決這個問題,提供了一個模板語言---JSX
JSX是一種JS擴展語言。容許在JS中以標籤形式構建元素。而且JSX開發工具中還能夠具備各類提示和快捷鍵。
可以極大的提升開發效率
const element = ( <h1 className="greeting"> Hello, world! </h1> );
但JSX編寫的組件只是React.createElement()語法糖,打包編譯過程當中會將JSX語法轉換爲React.createElement()
🐋🐋🐋 JSX編寫的組件本質是 React.createElement() 語法糖。因此React還支持使用 React.createElement() 建立虛擬DOM(Virtual DOM)。
🐋🐋 JSX是React提供構建代碼方式的一種擴展語言,本質是一個語法糖。JSX定義的事件、style、class是JSX自身語法,並非原生DOM。因此有些屬性名稱不一致。
🐋🐋 JSX轉換React.createElement()操做使用的是babel提供的一個plugin,在下面再介紹
🐋 JSX目前被社區承認。Vue@3.X也支持JSX
React目前最新版本爲17.0.1,在這裏就直接引用此版原本介紹,對React有興趣的朋友在從老版本循循漸進的學習。
yarn add react@17.0.1
react庫是React的核心庫,具備 React.createElement() 、虛擬DOM、JSX語法支持等一系列核心內容。
可是此庫並不沒有提供與真實DOM交互。與真實DOM交互的代碼則由react-dom提供,
yarn add react-dom@17.0.1
react相似一個通用庫,沒有與任何平臺具備相關性,只負責組織數據結構。
就像寫React Native時,使用了react-native來作平臺交互。
接下來就仿照react-cli來組織代碼。
第一步就是在HTML頁面中建立一個元素做爲React承載的根節點。
🐋 vue-cli也具備這麼一個根節點用來承載vue,只不過元素ID名稱不同,有興趣的朋友能夠自行查看。
接下來處理JS,在以前打包測試中都是使用 /src/index.js 文件做爲源文件。
也是使用此文件做爲源文件。
🐋🐋 React只是承載在打包器中的一個應用框架。通過打包器打包將JSX轉換爲可運行的代碼。
import React from 'react'; import ReactDOM from 'react-dom'; const root = ( <h1 className="greeting"> Hello, world! </h1> ); ReactDOM.render(root, document.getElementById('root'));
在 /src/index.js 文件中使用了JSX建立元素,而後使用
react-dom中的 ReactDOM.render() 添加到根節點中。
🐋 vue-cli也一樣如此,有興趣的朋友能夠自行查看
不過若是此時執行yarn build
操做,會直接報錯。
這是由於JSX沒法被識別的問題。前面說過,JSX只是React提供的一種模板語言。本質上並不屬於JS模塊。
因此須要將JSX轉換爲 React.createElement() 形式
提供這個轉換操做的是babel中提供的一個plugin:@Babel/plugin-syntax-jsx
不過不須要直接安裝這個plugin
babel爲React提供了一個preset:@babel/preset-react。
@babel/preset-react中封裝了全部處理React的plugin
yarn add -D @babel/preset-react@7.12.13
🐋 Babel官網提供了JSX轉換爲 React.createElement() 的測試,有興趣的朋友能夠測試測試
而後配置在 .babelrc 文件中
此時執行yarn build
即可以執行成功,而且查看生成代碼能夠看到JSX已經轉換爲了React.createElement()
在瀏覽器也能夠正常運行代碼
React代碼已經運行成功,接下來就組織React代碼。
剛纔,直接在 /src/index.js 文件中編寫了JSX代碼進行測試
可是真正開發中,須要將JSX代碼編寫在 .jsx 文件中,經過模塊導入導入方式提供給 /src/index.js 文件。
將JSX提取到 /src/app.jsx 文件,在 /src/index.js 導入。
🐋🐋 app.jsx做爲React框架的根節點。用在承載React組件。
/src/app.jsx 文件中組件做爲React的根節點。React也是以樹的組織方式管理,/src/app.jsx 文件中組件就是樹根。React框架代碼就像 託管 在了 /src/app.jsx 之中
🐋 🐋
- React組件分爲 函數組件 和 類組件 , 函數組件 方便,再加上 Hooks 的助力,在編寫顆粒度較小組件時使用 函數組件 是個很是好的選擇。類組件 封裝性強,內部提供完善的鉤子函數和一系列功能,再加上繼承特性。比較適合使用在業務代碼主幹中。
- /src/app.jsx 中返回的 <></> 表明 空標籤 ,React組件只容許返回一個元素,但有時候組件須要返回元素數組,能夠在外部包一層空標籤。與Vue中的template標籤功能一致。
- React 組件名稱約定爲大寫形式。
.jsx做爲一種新的文件格式,須要在webpack進行配置使用babel
const modules = { module:{ rules:[ { // 全部的.js或者.jsx文件都走babel-loader test: /\.js(x?)$/, include: path.join(config.root,'src'), loader: "babel-loader" } ] } }
而且能夠提供引用時忽略後綴名稱。
resolve:{ // 可被忽略的後綴 extensions:['.jsx', '.js', '.json'], }
此時就算成功將React使用在腳手架中了。
而對於React Router、Redux只是用於擴展React的開發庫。在此就再也不添加。
🐋 vue-cli搭建方式與react-cli基本一致,只是各自框架暴露的API不一樣
在介紹babel時使用過package.json文件中browserslist屬性設置瀏覽器版本,那麼browserslist屬性究竟是怎麼回事呢?
前面介紹過,前端的運行環境(瀏覽器)版本是由用戶決定的,不一樣的項目對於瀏覽器版本要求不同。
而在打包過程當中。須要指定支持的瀏覽器版本,以這些版本對開發代碼作出適配。(CSS、JS都須要適配)。
browserslist屬性就是提供指定瀏覽器版本功能。是由browserslist庫提供的。
而這個簡單的功能browserslist卻作出了強大的效果,獲得了社區的高度承認。不少庫都直接依賴browserslist
browserslist提供了兩種配置方式。
一種就是配置在package.json文件中的browserslist屬性。browserslist執行時會默認讀取此屬性。
另外一種是使用約定文件。能夠在項目根目錄(package.json所在目錄)建立一個約定文件 .browserslistrc.json ,將屬性配置在此。.browserslistrc.json文件名稱通常會省略後綴:.browserslistrc
兩種方式不可同時設置,不然會直接報錯。
我的推薦直接配置在package.json文件中,不必建立一個文件了。在此也就直接使用此方案。
browserslist可使用不用屬性來靈活的控制瀏覽器版本。
以下所示。能夠設置在不一樣環境下設置不一樣瀏覽器版本。
"browserslist": { "development": [ "chrome > 75" ], "production": [ "ie 9" ] }
屬性值取自Node.js中環境變量。環境變量名稱爲BROWSERSLIST_ENV。因此須要設置環境變量。
注意:在此雖然設置在webpack.config.js文件中,但設置的是Node.js中的環境變量, 並非webpack提供的環境變量。
browserslist屬性值名稱能夠隨意命名。只要與Node.js中BROWSERSLIST_ENV環境變量對應便可。
在此就不貼圖測試了,有興趣的朋友能夠自行測試。
至於BROWSERSLIST_ENV 環境變量與 webpack中不一樣模式的關聯,在下一篇介紹。
browserslist支持設置當前基本上全部的瀏覽器,在Github上做者說明了能夠設置的瀏覽器
能夠看到,browserslist幾乎支持全部瀏覽器:PC、安卓、IOS 甚至還有國內瀏覽器。
🐋🐋 設置瀏覽器時名稱不區分大小寫
browserslist能獲得社區的承認,也就在於browserslist提供了強大的屬性設置。
如前面使用的 指定 區間瀏覽器(chrome > 75) 也只是browserslist簡單的屬性配置
下面簡單列舉部分browserslist屬性配置,想了解更多的朋友請參考Github
defaults:browserslist設置的默認瀏覽器版本。屬性只至關 > 0.5%, last 2 versions, Firefox ESR, not dead
指定版本號: 支持直接指定某個瀏覽器版本號。
IE 11:設置IE11瀏覽器
範圍版本:支持設置某個瀏覽器指定範圍版本。
Chrome > 75: 設置大於Chrome75版本的瀏覽器
而且支持 >=
、<
、<=
語法設置
24個月內未更新版本:支持設置24個月內未更新的版本
dead
瀏覽器使用率:支持設置指定瀏覽器使用率版本
>5%:全球超過5%人使用的瀏覽器版本
> 5% in US:美國超過5%使用的瀏覽器版本
> 5% in alt-AS:亞洲超過5%使用的瀏覽器版本
也自定義設置地區,具體參考Github文檔
而且支持 >=
、<
、<=
語法設置
最新瀏覽器版本:支持設置最新的幾個版本瀏覽器。
last 2 versions:設置全部瀏覽器最新的兩個版本。
last 2 Chrome versions:設置Chrome瀏覽器最新的兩個版本
排除瀏覽器:browserslist支持排除指定瀏覽器,
not ie < 11:排除IE11如下的瀏覽器
條件組合:browserslist強大的功能之一是支持多個條件作一個,這也是browserslist靈活所在。
例如
"browserslist": [ "ie 9", "Chrome > 75" ],
這就是一個而且(and)組合設置。二者都必須知足
browserslist一樣支持 或者(or)組合:> .5% or last 2 versions
通常只須要簡單的設置便可。
🐋🐋🐋
React是一個快速構建高性能網站的開發框架
React使用了虛擬DOM(Virtual DOM)和diff 算法優化了DOM操做
React利用自定義DOM類型解耦平臺限制,以此實現了跨平臺
JSX只是一個JS擴展語法。React使用JSX做爲構建元素的模板語言
browserslist是一個強大的設置瀏覽器版本庫。
{ "name": "my-cli", "version": "1.0.0", "main": "index.js", "author": "mowenjinzhao<yanzhangshuai@126.com>", "license": "MIT", "devDependencies": { "@babel/core": "7.13.1", "@babel/plugin-transform-runtime": "7.13.7", "@babel/preset-env": "7.13.5", "@babel/preset-react": "7.12.13", "@babel/runtime-corejs3": "7.13.7", "babel-loader": "8.2.2", "clean-webpack-plugin": "3.0.0", "html-webpack-plugin": "5.2.0", "webpack": "5.24.0", "webpack-cli": "4.5.0" }, "dependencies": { "jquery": "3.5.1", "react": "17.0.1", "react-dom": "17.0.1" }, "scripts": { "start": "webpack --mode=development --config webpack.config.js", "build": "webpack --mode=production --config webpack.config.js" }, "browserslist": [ "ie 9", "Chrome > 75" ] }
const path = require('path') const webpack = require("webpack"); const HtmlWebpackPlugin = require('html-webpack-plugin') const { CleanWebpackPlugin } = require('clean-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') // browserslist環境變量 process.env.BROWSERSLIST_ENV = 'development' const config = { root: path.join(__dirname, './'), } const modules = { // 入口文件 // 字符串形式 entry: path.join(config.root, 'src/index.js'), // 對象形式 // entry:{ // 'index': path.join(config.root, 'src/index.js'), // }, // 輸出文件 // 字符串形式 // output:path.join(config.root, './dist/[name].js') //對象形式 output: { // 輸出文件的目錄地址 path: path.join(config.root, 'dist'), // 輸出文件名稱,contenthash表明一種緩存,只有文件更改纔會更新hash值,從新打包 filename: '[name]_[contenthash].js' }, //devtool:false, //'eval' module:{ rules:[ { // 全部的.js(x?)文件都走babel-loader test: /\.js(x?)$/, include: path.join(config.root,'src'), loader: "babel-loader" } ] }, optimization: { minimize: false, minimizer: [ new TerserPlugin({ // 指定壓縮的文件 include: /\.js(\?.*)?$/i, // 排除壓縮的文件 // exclude:/\.js(\?.*)?$/i, // 是否啓用多線程運行,默認爲true,開啓,默認併發數量爲os.cpus()-1 // 能夠設置爲false(不使用多線程)或者數值(併發數量) parallel: true, // 能夠設置一個function,使用其它壓縮插件覆蓋默認的壓縮插件,默認爲undefined, minify: undefined, // 是否將代碼註釋提取到一個單獨的文件。 // 屬性值:Boolean | String | RegExp | Function<(node, comment) -> Boolean|Object> | Object // 默認爲true, 只提取/^\**!|@preserve|@license|@cc_on/i註釋 // 感受沒什麼特殊狀況直接設置爲false便可 extractComments: false, // 壓縮時的選項設置 terserOptions: { // 是否保留原始函數名稱,true表明保留,false即保留 // 此屬性對使用Function.prototype.name // 默認爲false keep_fnames: false, // 是否保留原始類名稱 keep_classnames: false, // format和output是同一個屬性值,,名稱不一致,output不建議使用了,被放棄 // 指定壓縮格式。例如是否保留*註釋*,是否始終爲*if*、*for*等設置大括號。 format: { comments: false, }, output: undefined, // 是否支持IE8,默認不支持 ie8: false, compress: { // 是否使用默認配置項,這個屬性當只啓用指定某些選項時能夠設置爲false defaults: false, // 是否移除沒法訪問的代碼 dead_code: false, // 是否優化只使用一次的變量 collapse_vars: true, warnings: true, // 是否刪除全部 console.*語句,默認爲false,這個能夠在線上設置爲true drop_console: false, // 是否刪除全部debugger語句,默認爲true drop_debugger: true, // 移除指定func,這個屬性假定函數沒有任何反作用,可使用此屬性移除全部指定func // pure_funcs: ['console.log'], //移除console }, }, }) ] }, plugins: [ new HtmlWebpackPlugin({ // HTML的標題, // template的title優先級大於當前數據 title: 'my-cli', // 輸出的html文件名稱 filename: 'index.html', // 本地HTML模板文件地址 template: path.join(config.root, 'src/index.html'), // 引用JS文件的目錄路徑 publicPath: './', // 引用JS文件的位置 // true或者body將打包後的js腳本放入body元素下,head則將腳本放到中 // 默認爲true inject: 'body', // 加載js方式,值爲defer/blocking // 默認爲blocking, 若是設置了defer,則在js引用標籤上加上此屬性,進行異步加載 scriptLoading: 'blocking', // 是否進行緩存,默認爲true,在開發環境能夠設置成false cache: false, // 添加mate屬性 meta: {} }), new CleanWebpackPlugin({ // 是否僞裝刪除文件 // 若是爲false則表明真實刪除,若是爲true,則表明不刪除 dry: false, // 是否將刪除日誌打印到控制檯 默認爲false verbose: true, // 容許保留本次打包的文件 // true爲容許,false爲不容許,保留本次打包結果,也就是會刪除本次打包的文件 // 默認爲true protectWebpackAssets: true, // 每次打包以前刪除匹配的文件 cleanOnceBeforeBuildPatterns: ['**/*'], // 每次打包以後刪除匹配的文件 cleanAfterEveryBuildPatterns:["*.js"], }), new webpack.DefinePlugin({ "global_a": JSON.stringify("我是一個打包配置的全局變量") }), ], resolve: { alias:{ // 設置路徑別名 '@': path.join(config.root, 'src') , '~': path.join(config.root, './src/assets') , }, // 可互忽略的後綴 extensions:['.JSX', '.js', '.json'], // 默認讀取的文件名 mainFiles:['index', 'main'], } } // 使用node.js的導出,將配置進行導出 module.exports = modules
{ "presets": [ "@babel/preset-react", [ "@babel/preset-env", { "modules":false // 移除useBuiltIns設置 // "targets": "chrome > 75", // "useBuiltIns": "usage", // "corejs": { // "version": 3, // "proposals":true // } } ] ], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": { "version": 3, "proposals": true } } ] ] }