zoukankan      html  css  js  c++  java
  • webpack学习笔记(二)

    1 支持es6语法 

    (注意直接写map等es6语法,由于现代浏览器是支持的,所以可以显示出来,可以执行 npm run build 命令,在生成的文件中,查看剪头函数,promise,async/awiat等es6语法是没有被转成es5的)

    【注意 map方法是es5中的方法】

    1.1 首先安装 babel-loader和babel的核心库: npm install -D bable-loader @babel/core   bable-loader 是连接 webpack 和 babel 的桥梁

    module.exports = {
        module: {
            rules: [{ 
                test: /.js$/, 
                exclude: /node_modules/,  //排除这个文件夹下的内容 
                loader: 'babel-loader',
            }]
        }
    }

    1.2 安装    npm install @babel/preset-env -D     preset-env 包含了es6转换成es5的规则

    module.exports = {
        module: {
            rules: [{ 
                test: /.js$/, 
                exclude: /node_modules/, 
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env']  //在这里配置使用loader的配置项
                }
            }]
        }
    }

    1.3 此时诸如 let等已经转成H5 了,还有一些es6语法不能转过去,比如箭头函数,使用polyfill

     npm install --save @babel/polyfill

    1.4 在业务代码中引入: 

    import "@babel/polyfill"

    由于会把所有的转换方法打包,所以会导致js文件变的非常大,而我们只用了几个es6的语法

    module.exports = {
        module: {
            rules: [{ 
                test: /.js$/, 
                exclude: /node_modules/, 
                loader: 'babel-loader',
                options: {
                    presets: [['@babel/preset-env',{ //这里要给preset-env设置参数 所以要放在数组中
                        useBuiltIns:'usage' //根据业务代码使用的es6语法 来添加对应的转换方法,且使用改方法后 就是按需引用 polyfill 不用在文件中 再次引用 polyfill
                    }]]  
                }
            }]
        }
    }

    还可以配置转换的环境,进一步优化缩小打包代码:

    //针对各个浏览器的最新的两个版本,以及safari的版本7及以上,针对以上两个条件的兼容情况进行代码转译
    {
      "presets": [
        ["@babel/preset-env", 
        {
    "targets": { "browsers": ["last 2 versions", "safari >= 7"] } }]
    ]
    }

     1.5 由于babel的可配置项特别多,所以可以将其提取出来放在 .babelrc 文件中,然后把options的对象整个放在这个文件中

    注意1:

    根据babel的网址: https://www.babeljs.cn/repl  可以在线调试转成es5的情况

    左侧是es6的代码,选择转成es2015,转的范围是 chrome 67, 由于chrome支持es6语法,所以不用转译;

    上图选择的是 ie9 的环境,可见左侧的es6语法已经转译成右侧的es5语法了;

    new Promise 没有改变;map语法是es5的不用转变;

    ---------------------------------------

    注意2:使用了  "useBuiltIns":"usage" 之后,自动按需引入 pollyfill,所以文件中不用再引用 polyfill,

    首先在文件中使用es6语法:

    let listData = [1,2,3,4]
    listData.includes(4);

    示例1:

    {
      "presets":[
        ["@babel/preset-env",{
        "useBuiltIns":"false",
        "targets":{
            "browsers":[">1%","last 2 version","not ie <= 8"]
          }
        }]
      ]
    }

    写成false之后,不在引入 polyfill, 则编译后的结果:

    可以看到 includes 没有被转译

    示例2:

    {
      "presets":[
        ["@babel/preset-env",{
        "useBuiltIns":"usage",
        "targets":{
            "browsers":[">1%","last 2 version","not ie <= 8"]
          }
        }]
      ]
    }

    即按需引入 polyfill ,在看转译的文件,全局搜索 includes:

    全局有48个,includes,说明前面已经做过转译了!!!

    【注意:如果进行过代码分割,也就是生成了新的js文件来存放来自于 module_node 的代码,比如 vue babel等,需要在生成的vendor文件中查找。比如 async include 等es6 特性的方法,在业务代码中使用与否,在vendor文件中,查找该单词的数量确实不一样】

    -----------------------------------------

    注意3:

    1:

    Babel 7 的相关依赖包需要加上 @babel scope。一个主要变化是 presets 设置由原来的 env 换成了 @babel/preset-env, 可以配置 targetsuseBuiltIns 等选项用于编译出兼容目标环境的代码。其中useBuiltIns如果设为 "usage",Babel 会根据实际代码中使用的ES6/ES7代码,以及与你指定的targets,按需引入对应的 polyfill,而无需在代码中直接引入 import '@babel/polyfill'避免输出的包过大,同时又可以放心使用各种新语法特性。

    {
      "presets": [
        ["@babel/preset-env", {
          "modules": false,
          "targets": {
            "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
          },
          "useBuiltIns": "usage"
        }]
      ]
    }

    配置文件中:

    {
          test:/.js$/,
          exclude: __dirname + 'node_modules',
          use: 'babel-loader'  //注意这里要用的是 use 不要直接使用 loadder 否则转化es5 不生效!!!!!!!
    }

    2:    webpack4.x下babel的安装、配置及使用

    3: 如果打包时有下面的警告:

     npm install --save core-js@2

    需要配置corejs

    {
      "presets":[
        ["@babel/preset-env",{
        "useBuiltIns":"usage",
        "corejs":2,
        "targets":{
            "browsers":[">1%","last 2 version","not ie <= 8"]
          }
        }]
      ],
      "plugins": ["@babel/plugin-syntax-dynamic-import"]
    }
    开源库zloirock/core-js
    提供了es5、es6的polyfills,包括promises、symbolscollections、iterators、typed arraysECMAScript 7+ proposalssetImmediate 等等。
    如果使用了 babel-runtime、babel-plugin-transform-runtime 或者 babel-polyfill,你就可以间接的引入了 core-js 标准库

    3: 配置打包时报Cannot read property 'bindings' of null 或 Cannot find module '@babel/core'问题

    解决:模块中对js的处理配置如下图可解决

        

    或者使用:

    {
                test:/.js$/,
                exclude: /('node_modules')/,
                use: 'babel-loader'
      }

    2 使用webpack 搭建react的运行环境

    2.1  首先安装react  npm install react react-dom --save 

    npm install --save-dev @babel/preset-react

    然后在 .babelrc

    {
        presets:[
            [
                "@babel/preset-env",{
                    targets:{
                        chrome:"67",
                    },
                    useBuiltIns: 'usage'
                }
            ],
            "@babel/preset-react"  //执行顺序是 从下往上 从右往左
        ]
    }

    注意的是:

    如果在babel的配置中写了    useBuiltIns: 'usage'  (只使用用到了的方法) 则会自动引入 import  "@babel/polyfill"  所以不需要在业务代码中再次引入。

    3 tree shaking 只打包引用的文件代码,例如定义 build.js:

    export const add = (a,b)=>{
        console.log(a+b)
    }
    
    export const minus = (a,b)=>{
        console.log(a-b)
    }

    在其他文件中引入:import { add } from './build.js' 

    则在生成的文件中,你会发现即使没有调用 minus方法,也会把整个的build.js 进行打包编译。为了只对使用过的方法进行打包,进行如下操作:

    首先明确的是  tree shaking 只对  import { add } from './build.js'  起作用,而对  const add = require ('./build.js') 不起作用

    注意的是:export和export default的区别:

    export default 只能导出一个对象,该对象可以包含多个变量,但是不能包含多个函数,例如不能向下面这样:

    const myadd = (a,b)=>{
      console.log('myadd');
      return a+b;
    }
    const myminus = (a,b)=>{
      console.log('myminus');
      return a-b;
    }
    
    export default{
      myadd,
      myminus
    }

    export 可以导出多个变量和函数,对应的按需引入import { add } from './build.js' 

    module.exports = {
        mode: 'development',
        module: {
        },
        plugins: [
        ],
        optimization: {
            usedExports: true //在这里进行配置
        },
        output: {
        }
    }

    但是这样的话对于 没有 引入具体 函数的:  import '@babel/polly-fill'  会不进行打包

    所以要在package.json配置文件中 添加: "sideEffects":["@babel/polly-fill"] ,即对不需要使用 tree-shaking 进行操作的文件,放在这里,

    但是一般我们不需要在文件中 引入这些的话 ,需要tree-shaking 对所有import的文件进行 按需处理 ,所以这里写成false : "sideEffects":false 

    但是对于没有具体引出的 css: import './index.css' ,json配置文件中需要重新定义: "sideEffects":["*.css"] 

    最后 对于开发模式,虽然能够识别,但是便于开发调试,没有删掉未引用的代码,在上线的模式 中:

    mode: 'development', //开发环境
    
    mode: 'production' // 上线环境
    // tree-shaking 已经把 optimization 写好了 
    // 所以删除
    // optimization: {
    //     usedExports: true //在这里进行配置
    // },

    然后搜索打包后的js文件,发现没有引入的方法不再有了。

    树摇 只能处理 export = {} 然后 import {*} from '/';的代码;比如 import {join} from 'lodash';
    则无法处理,因为其内部不是用标准的 export 导出的
     
    import {join} from 'lodash';
    const myadd = (a,b)=>{
      console.log('myadd');
      return a+b;
    }
    const myminus = (a,b)=>{
      console.log('myminus');
      return a-b;
    }
    
    const myjoin = (a,b)=>{
      console.log('myjoin');
      return join([a,b],'@@')
    }
    export{
      myadd,
      myminus,
      myjoin
    }

    例如上面,引入的 lodash库,并不能随着树摇而去掉。

     
     
     

    4 对webpack的配置文件进行模式区分:新建两个配置文件,其中 webpack.dev.js 的 mode是 mode: 'development' 

    {
        "scripts":{
            "dev":"webpack-dev-server --config webpack.dev.js",
            "build":"webpack --config webpack.prod.js"
        }
    }

    4.1 由于两个文件重复性很大,所以把共有的代码放在新建的文件中: webpack.common.js;

    这样就会有三个配置文件 分别是 共性的代码  webpack.common.js;开发的配置文件 webpack.dev.js;上线的文件 webpack.prod.js。

    然后使用webpack-merge进行文件的合并: npm install webpack-merge -D 

    const merge = require('webpack-merge');
    const commonConfig = require('./webpack.common.js');
    
    const devConfig = {
        mode:'',
        devtool:'',
        devServer:{
    
        }
    }
    module.exports = merge(commonConfig,devConfig); 
    //这样使用 merge 合并后导出 commonConfig 和 devConfig

     如果把这三个配置文件放在build文件夹下,则config.json中相应的修改:

     "dev":"webpack-dev-server --config ./build/webpack.dev.js" 

       同样配置文件中也要修改:

    {
        //CleanWebpackPlugin插件会默认当前文件为根路径,
        //所以需要用root参数,配置当前的根路径
        plugins:[
            new CleanWebpackPlugin(['dist'],{  //dist路径是在root相对下的路径
                root:path.resolve(__dirname,'../') 
            })
        ],
        output:{
            filename:'[name].js',
            path:path.resolve(__dirname,'../dist') //这做修改里也要
        }
    }

    5. code Splitting 即代码拆分  比如要提取出公共的 lodash代码

    5.1 首先安装  npm install lodash --save  该库可以用来操作一些字符串,

    比如:

    import _ from 'lodash';
    var element = document.createElement('div');
    element.innerHTML = _.join(['Dell', 'Lee'], '-');
    document.body.appendChild(element);

    然后在配置文件中,在和entry,module等并列的位置:

    optimization:{
      splitChunks:{
          chunks:'all'
      }
    }

    就可以自动打包出vendor.js;

    5.2 异步引入的js库

    function getComponent() {
        return import('lodash').then(({ default: _ }) => {
            var element = document.createElement('div');
            element.innerHTML = _.join(['Dell', 'Lee'], '-');
            return element;
        })
    }
    
    getComponent().then(element => {
        document.body.appendChild(element);
    });

    但是打包不支持,所以安装: npm install babel-plugin-dynamic-import-webpack //动态引入import 

    因为引入了新的babel,所以要在babelrc文件中修改:

    {
        presets: [
            [
                "@babel/preset-env", {
                    targets: {
                        chrome: "67",
                    },
                    useBuiltIns: 'usage'
                }
            ],
            "@babel/preset-react"
        ],
       plugins: ["dynamic-import-webpack"] //修改了这里
    }

    代码分割和webpack无关,

    webpack 中实现代码分割,两种方式:

    1. 同步代码: 只需要在webpack.common.js 中做 optimization 的配置

    2. 异步代码: 无需做任何配置需要安装上述插件;

    以上promise代码还可以简化成async模式:

    async function getComponent() {
      const { default:_ } = await import( /* webpackChunkName:"lodash" */ 'lodash' );
      var element = document.createElement('div');
      element.innerHTML = _.join(['Dell', 'Lee'], '-');
      return element;
    }
    
    getComponent().then(element => {
     document.body.appendChild(element);
    });

    5.3 以上不是官方插件,所以不要看上面的了,看下面的官方插件(在babel的官网上):

    首先安装: npm install --save-dev @babel/plugin-syntax-dynamic-import    然后在.babelrc文件中增加

    plugins: ["@babel/plugin-syntax-dynamic-import"]

    然后使用方式为:

    function getComponent() {
        return import(/* webpackChunkName:"lodash" */ 'lodash').then(({ default: _ }) => {
            var element = document.createElement('div');
            element.innerHTML = _.join(['Dell', 'Lee'], '-');
            return element;
        })
    }
    
    getComponent().then(element => {
        document.body.appendChild(element);
    });

    config文件中:

    optimization: {
            splitChunks: {
          chunks: 'all',
          cacheGroups: {
            vendors:false ,
            default:false
          }
        }
    }

    更多配置:

    optimization: {
      splitChunks: {
        chunks: 'all',//同步异步全都打包
        minSize: 30000,//打包的库或者文件必须大于这个字节才会进行拆分
        minChunks: 1,//规定当模块在生成的js文件(trunk)中被调用过多少次的时候再进行拆分
        maxAsyncRequests: 5,
        maxInitialRequests: 3,
        automaticNameDelimiter: '~',//如果不写filename 默认名字 组名~[name]
        name: true,
        cacheGroups: {//缓存组,因为需要打包完成之后,在把所有要拆分的代码合并拆分,所以先要缓存
          vendors: {
            test: /[\/]node_modules[\/]/, //如果上面chunks定为all,就是找到所有的import文件,看他是不是调用于 node_modules 文件夹 是的话就拆分
            priority: -10,//优先级 比如同时符合vender 和 default 这个优先级高 所以存在这里
            filename: 'vendors.js', //拆分后打包的文件名字
          },
          default: {//像文件中 import进来的文件 如果不在 node_modules文件夹中 则走默认组,打包出的文件名字是 common.js
            priority: -20,
            reuseExistingChunk: true,//比如a.js 引用了 b.js;如果b.js在之前已经被拆分过,则这里不再对其进行拆分
            filename: 'common.js'
          }
        }
      }
    }
  • 相关阅读:
    软件设计中的分层模式, 三层开发遵循的原则,分层开发的特点和优势
    什么是jsp?
    在Servlet中如何如何获取请求的参数?
    Servlet的加载(执行过程,原理)和生命周期
    servlet的注册
    什么是servlet容器
    什么是Servlet
    如何访问动态页面——URL
    什么是C/S? Client/server的简写,这里Server指的是DBServer。
    MVC(Model-View-Controller)软件设计模式
  • 原文地址:https://www.cnblogs.com/xiaozhumaopao/p/10658920.html
Copyright © 2011-2022 走看看