首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从零学脚手架(五)---react、browserslist

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

作者头像
莫问今朝
发布2021-03-16 15:29:14
1.3K0
发布2021-03-16 15:29:14
举报
文章被收录于专栏:博客园博客园

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

react

react介绍

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

而是直接介绍React

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

? 以前都说是“三大框架”,还有一个Google开发的Angular,但是国内Angular使用份额越来越少。 个人感觉Angular主要问题是上手成本。Angular比较偏向于后端,很多概念对于前端开发人员都是噩梦。不过对于前端工程化,个人认为Angular是集大成之作。个人建议,对于有经验的朋友,可以稍微学习下Angular中的思想。

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

React本身是一个特别简单的库:将元素抽象为虚拟DOM,更新DOM时对比虚拟DOM,然后只更新那些真正需要更新的元素。

React.createElement()

使用Document构建DOM时,都是使用 document.createElement() 来构建标签

const li =  document.createElement('li');
document.body.appendChild(li)

在React中, 也提供了这样一个自定义函数来React组件。

React.createElement() 返回的是一个React自定义的元素类型:ReactElement

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)。

?? JSX是React提供构建代码方式的一种扩展语言,本质是一个语法糖。JSX定义的事件styleclass是JSX自身语法,并不是原生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

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()

在浏览器也可以正常运行代码

.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 Router、Redux只是用于扩展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.js中BROWSERSLIST_ENV环境变量对应即可。

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

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

browserslist支持的浏览器

browserslist支持设置当前基本上所有的浏览器,在Github上作者说明了可以设置的浏览器

可以看到,browserslist几乎支持所有浏览器:PC、安卓、IOS 甚至还有国内浏览器。

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

browserslist属性

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

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

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

一般只需要简单的设置即可。

总结

???

  • 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
        }
      }
    ]
  ]
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-03-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 如果此篇对您有所帮助,在此求一个star。项目地址: OrcasTeam/my-cli
  • react
    • react介绍
      • React.createElement()
    • 虚拟DOM
      • JSX
    • 添加 React
      • 安装 react
      • 使用 react
      • .jsx文件
  • browserslist
    • browserslist是什么
      • browserslist配置方式
        • browserslist环境变量
          • browserslist支持的浏览器
            • browserslist属性
            • 总结
            • 本文参考
            • 本文依赖
            • package.json
            • webpack.config.js
            • .babelrc
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档