从零学脚手架(五)---react、browserslist

若是此篇对您有所帮助,在此求一个star。项目地址: OrcasTeam/my-cli

react

react介绍

目前,国内主流的前端应用框架具备两个:vue.jsreact.js,关于vue和react的优劣性,网上众说纷纭。在下就不在此引战。html

而是直接介绍React前端

🐋🐋🐋 vueReact这种都是快速应用开发工具,可能也会像曾经如日中天的JQuery被市场淘汰,因此我的建议不要盲目只追求快速工具的使用,而是花时间去学习原点。例如设计思想数据结构。快速应用框架(或语言)只不过是应用工具而已。vue

🐋 之前都说是“三大框架”,还有一个Google开发的Angular,可是国内Angular使用份额愈来愈少。node

我的感受Angular主要问题是上手成本。Angular比较偏向于后端,不少概念对于前端开发人员都是噩梦。不过对于前端工程化,我的认为Angular是集大成之做。我的建议,对于有经验的朋友,能够稍微学习下Angular中的思想。react

React是一个用于构建用户界面的 JavaScript 库,jquery

React自己是一个特别简单的库:将元素抽象为虚拟DOM,更新DOM时对比虚拟DOM,而后只更新那些真正须要更新的元素。webpack

React.createElement()

使用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可以跨平台的缘由也在于此。

在平常开发中,也会常常写无关业务的通用封装,其思想与此相似。

虚拟DOM

在直接使用Document更新DOM元素时,不少时候会由于某些缘由 对没必要更新DOM进行更新 从而产生了性能浪费

解决这个问题通常想到的作法就是作一个DOM缓存。建立DOM时将DOM信息缓存,更新时对比新旧DOM。排除掉没必要要的更新DOM。

这种缓存DOM数据的方案就叫虚拟DOM(Virtual DOM), 而排除算法叫作diff算法

React也使用了这种方案提高性能

虚拟DOM(Virtual DOM)diff算法 是对数据结构和算法的考验。每个人均可以模拟出简单的方案,但不是每个人均可以写出优秀的解决方案。

在下愚钝,对于数据结构和算法掌握的很差。因此对虚拟DOM(Virtual DOM)diff算法只有浅薄的认知。有兴趣的朋友能够看一下这篇文章:深度剖析:如何实现一个 Virtual DOM 算法

JSX

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)

🐋🐋 JSXReact提供构建代码方式的一种扩展语言,本质是一个语法糖。JSX定义的事件styleclassJSX自身语法,并非原生DOM。因此有些属性名称不一致。

🐋🐋 JSX转换React.createElement()操做使用的是babel提供的一个plugin,在下面再介绍

🐋 JSX目前被社区承认。Vue@3.X也支持JSX

添加 React

安装 react

React目前最新版本为17.0.1,在这里就直接引用此版原本介绍,对React有兴趣的朋友在从老版本循循渐进的学习。

yarn add react@17.0.1

react库是React的核心库,具备 React.createElement()虚拟DOMJSX语法支持等一系列核心内容。

可是此库并不没有提供与真实DOM交互。与真实DOM交互的代码则由react-dom提供,

yarn add react-dom@17.0.1

react相似一个通用库,没有与任何平台具备相关性,只负责组织数据结构。

就像写React Native时,使用了react-native来作平台交互。

使用 react

接下来就仿照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也一样如此,有兴趣的朋友能够自行查看

@babel/preset-react

不过若是此时执行yarn build操做,会直接报错。

这是由于JSX没法被识别的问题。前面说过,JSX只是React提供的一种模板语言。本质上并不属于JS模块。

因此须要将JSX转换为 React.createElement() 形式

提供这个转换操做的是babel中提供的一个plugin@Babel/plugin-syntax-jsx

不过不须要直接安装这个plugin

babelReact提供了一个preset@babel/preset-react

@babel/preset-react中封装了全部处理Reactplugin

yarn add -D @babel/preset-react@7.12.13

🐋 Babel官网提供了JSX转换为 React.createElement() 的测试,有兴趣的朋友能够测试测试

而后配置在 .babelrc 文件中

此时执行yarn build即可以执行成功,而且查看生成代码能够看到JSX已经转换为了React.createElement()

在浏览器也能够正常运行代码

.jsx文件
app.jsx

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 组件名称约定为大写形式
webpack配置

.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 RouterRedux只是用于扩展React的开发库。在此就再也不添加。

🐋 vue-cli搭建方式与react-cli基本一致,只是各自框架暴露的API不一样

browserslist

browserslist是什么

在介绍babel时使用过package.json文件中browserslist属性设置浏览器版本,那么browserslist属性究竟是怎么回事呢?

前面介绍过,前端的运行环境(浏览器)版本是由用户决定的,不一样的项目对于浏览器版本要求不同。

而在打包过程当中。须要指定支持的浏览器版本,以这些版本对开发代码作出适配。(CSS、JS都须要适配)。

browserslist属性就是提供指定浏览器版本功能。是由browserslist库提供的。

而这个简单的功能browserslist却作出了强大的效果,获得了社区的高度承认。不少库都直接依赖browserslist

browserslist配置方式

browserslist提供了两种配置方式。

一种就是配置在package.json文件中的browserslist属性。browserslist执行时会默认读取此属性。

另外一种是使用约定文件。能够在项目根目录(package.json所在目录)建立一个约定文件 .browserslistrc.json ,将属性配置在此。.browserslistrc.json文件名称通常会省略后缀:.browserslistrc

两种方式不可同时设置,不然会直接报错。

我的推荐直接配置在package.json文件中,不必建立一个文件了。在此也就直接使用此方案。

browserslist环境变量

browserslist可使用不用属性来灵活的控制浏览器版本。

以下所示。能够设置在不一样环境下设置不一样浏览器版本。

"browserslist": {
    "development": [
        "chrome > 75"
    ],
     "production": [
         "ie 9"
     ]
}

属性值取自Node.js中环境变量。环境变量名称为BROWSERSLIST_ENV。因此须要设置环境变量。

注意:在此虽然设置在webpack.config.js文件中,但设置的是Node.js中的环境变量, 并非webpack提供的环境变量。

browserslist属性值名称能够随意命名。只要与Node.jsBROWSERSLIST_ENV环境变量对应便可。

在此就不贴图测试了,有兴趣的朋友能够自行测试。

至于BROWSERSLIST_ENV 环境变量与 webpack中不一样模式的关联,在下一篇介绍。

browserslist支持的浏览器

browserslist支持设置当前基本上全部的浏览器,在Github上做者说明了能够设置的浏览器

能够看到,browserslist几乎支持全部浏览器:PC、安卓、IOS 甚至还有国内浏览器。

🐋🐋 设置浏览器时名称不区分大小写

browserslist属性

browserslist能获得社区的承认,也就在于browserslist提供了强大的属性设置。

如前面使用的 指定 区间浏览器(chrome > 75) 也只是browserslist简单的属性配置

下面简单列举部分browserslist属性配置,想了解更多的朋友请参考Github

  • defaultsbrowserslist设置的默认浏览器版本。属性只至关 > 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是一个强大的设置浏览器版本库。

本文参考

本文依赖

package.json

{
  "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"
    ]
}

webpack.config.js

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

.babelrc

{
  "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
        }
      }
    ]
  ]
}
相关文章
相关标签/搜索