目前,国内主流的前端应用框架具备两个: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 } } ] ] }