• webpack的世界


    本文也是多次学习webpack积累下来的知识点,一直在云笔记里。

    webpack的原理

    webpack构建流程
    从启动webpack构建到输出结果经历了一系列过程,它们是:

    • 解析webpack配置参数,合并从shell传入和webpack.config.js文件里配置的参数,生产最后的配置结果。
    • 注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应。
    • 从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去。
    • 在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。
    • 递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk。
    • 输出所有chunk到文件系统。

    需要注意的是,在构建生命周期中有一系列插件在合适的时机做了合适的事情,比如UglifyJsPlugin会在loader转换递归完后对结果再使用UglifyJs压缩覆盖之前的结果

    配置webpack就是在配置一个node的模块module.exports用于webpack来读取

    1、webpack与Rollup的优缺点和区别?

    webpack的优点:

    • 专注于处理模块化的项目,能做到开箱及用、一步到位
    • 可用plugin扩展,完整好用又不失灵活
    • 使用场景不限于web开发
    • 社区庞大

    webapck的缺点:

    • 只能用于采用模块化开发的项目

    Rollup

    • 可以对es6进行 Tree Shaking
    • 它支持导出ES模块的包
    • 它支持程序流分析,能更加正确的判断项目本身的代码是否有副作用

    2、devserver的使用

    webpack-dev-server是一个启动服务的程序,可以自动去,读取webpack.config.js 的文件。执行webpack-dev-server来启动,就可以来自动刷新页面,内部的原理是express的启服务,用webSocket来通知浏览器。

    3、webapck配置选项

    • Entry:入口,webpack执行构建的第一步从entry开始
    • Module:模块,一切都是模块,一个模块对应一个文件。webapck从entry开始递归找出依赖的模块。
    • Chunk:代码块,一个chunk由多个模块组合而成,用于代码合并与分割。
    • Loader:模块转换器,将模块的原内容按照要求转换成新的内容。
    • Plugin: 扩展插件,在特定时机执行
    • Output:输出结果,把处理的最终想要的代码输出结果

    4、Entry的配置

    1、context是webpack寻找相对路径文件会以context为根目录
    module.exports = {
        context: path.resolve(__dirname, './app'),
        entry:'./main.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, './dist')
        }
    }
    
    2、Entry
    • string 'app/'
    • array ['app/a.js', 'app/b.js'] 多入口,单Chunk
    • object [a:'a.js', b:'b.js'] 配置多个入口,每个入口生成一个Chunk

    也可以动态的配置,就是返回函数,可以同步也可以异步

    同步
    entry:()=>{
            return {
                a: './pages/a',
                b:'./pages/a'
            }
        },
    异步
        entry:new Promise((resolve)=>{
            resolve({
                a: '/a.js',
                b:'/b.js'
            })
        }),
    

    5、Output配置

    output是一个对象,里面有配置项

    1. filename 输出文件的名称,是string
    • [id Chunk的唯一标识,从0开始]
    • [name Chunk的名称]
    • [hash] Chunk的唯一标识Hash[hash:8]可以指定长度
    • [chunkhash] Chunk内容的Hash值,比如 ExtractTextWebapckPlugin就是用的那个
    1. chunkFilename 配置无入口的Chunk的输出名字,比如CommonChunkPlugin import('path/')动态加载
    2. path输出文件存在的本地目录:path: path.resolve(__dirname, './dist')
    3. publicPath 配置上传cnd的路径
    4. corssOriginLoading 在异步加载是,利用JONSP的原理动态插入script,用来控制crossorigin的值
      当本地尝试使用 window.onerror 去记录脚本的错误时,跨域脚本的错误只会返回 Script error。
      HTML5 新的规定,是可以允许本地获取到跨域脚本的错误信息,但有两个条件:
    • 一是跨域脚本的服务器必须通过 Access-Controll-Allow-Origin 头信息允许当前域名可以获取错误信息
    • 二是当前域名的 script 标签也必须指明 src 属性指定的地址是支持跨域的地址,也就是 crossorigin 属性

    可以取一下值:

    • anonymous(默认) 匿名的,加载不会带上用户的cookies
    • use-credentials,在加载脚本是会带上用户的cokires
    1. libraryTarget 和 library
    2. 构建一个可以被导入使用的库
    • libraryTarget 何种方式,var(默认)comomjs/commonjs2/this/window/global
    • library 导出的名字

    6、module

    • rules来配置Loader
    rules: [{
        test: '/.js$/',//命中文件
        use: ['babel-loader?cacheDirectory'],//指定loader  解析是从后向前
        include: path.resolve(__dirname, 'src'),//包括 
        exclude: path.resolve(__dirname, 'node_modules')//排除
      },{
        test: /.vue$/,
        use: ['vue-loader']
      }]
      
      
      vue需要使用vue-loader
    
    • noParse 来忽略没有采用模块化的文件的递归
    
            noParse: /jquery|chartjs/ //一般是正则 被忽略的文件不应该有import/requie/define等
    
    • parse可以细颗粒度配置解析比如AMD,commonJS等

    7、resolve 配置webpack如何寻找模块

    resolve: {
        alias: {
            components : './src/components'
        },
        mainFields: ['jsnext:main', 'browser', 'main'],//jsnext 是支持ES6的模式进入   
        extension: ['.ts','.js','.json'],//后缀列表 //延长;延期
        modules: ['./src/components', 'node_modules'],//指定本地解析 import 'button'
        enforceExtension: true,//开启必须带后缀  
    },
    

    8、plugin 接受数组,plugin的实例

    9、devServer 使用devserver启动还生效

    devServer:{
        hot: true,//启用 webpack 的模块热替换特性
        inline: true,//fasle是 iframe 模式
        historyApiFallback: true,//spa history的模式,
            任何请求都会返回index.html 也可以使用重写rewrites来详细配置
        contentBase:[path.join(__dirname, "public"),
            path.join(__dirname, "assets")],//提供静态文件 string fales关闭 array
        host:'0.0.0.0',//服务器外部可访问
        port: '9090',//端口
        disableHostCheck: true,//关闭host,devServer默认是接受本地请求配合host使用
        https:true,//开启https或者自己导入证书
        compress: true,//默认false开启gzip压缩   压缩
        open: true,//打开浏览器
        proxy:{ //   credentials 证书  设置成include,表示允许跨越传递cookie
            "/api": "http://localhost:3000"
        }
    },
    

    10、其他配置项

    target:'web',//构建到的环境比如node web webworker等
        devtool:'source-map',//  可以设置为false
        watch:true,//监听文件,默认是关闭的devserer默认打开
        externals:{
            jquery: 'jQuery'
        },
        resolveLoader:{ //加载本地loader
    },
    

    11、为单页应用生成html

    使用 html-webpack-plugin

    12、其他常用Loader

    file-loader 将js和css中的图片等替换成正确的地址,输出在文件中
    url-loader 可以将文件的内容经过base64编码后注入js或者css中,但是要限制大小

    {
            exclude: [
              /.(js|jsx)(?.*)?$/,
              /.(css|scss)$/,
              /.json$/,
              /.bmp$/,
              /.jpe?g$/,
              /.png$/,
            ],
            loader: require.resolve('file-loader'),
            options: {
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
          {
            test: [/.bmp$/, /.jpe?g$/, /.png$/],
            loader: require.resolve('url-loader'),
            options: {
              limit: 10000,
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },
    

    raw-loader 和 svg-inline-loader 可以把svg内嵌到网页,把svg当图片用,可以用上面的。

    13、Source Map

    以方便在浏览器通过代码调试

    devtool有很多的取值,由一下6个关键字随意组合而成

    • eval: 用eval语句包裹需要安装的模块

    • source-map:生成独立的Source Map 文件

    • hidden:不在javascript文件中支出source map 文件的所在,这样浏览器就不会自动加载source map

    • inline:将生成的source map转成base64格式内嵌在javascript文件中

    • cheap:在生成的source map中不会包含列信息,那样计算量更小,输出文件更小,同时loader输出的source map不会被采用

    • module:来时loader的source map 被简单处理成每行一个模块

    • eval: 生成代码 每个模块都被eval执行,并且存在@sourceURL

    • cheap-eval-source-map: 转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl

    • cheap-module-eval-source-map: 原始代码(只有行内) 同样道理,但是更高的质量和更低的性能

    • eval-source-map: 原始代码 同样道理,但是最高的质量和最低的性能

    • cheap-source-map: 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用

    • cheap-module-source-map: 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射

    • source-map: 原始代码 最好的sourcemap质量有完整的结果,但是会很慢

    eval和.map文件都是sourcemap实现的不同方式,虽然大部分sourcemap的实现是通过产生.map文件, 但并不表示只能通过.map文件实现。下面是eval模式后产生的模块代码

    cheap关键字的配置中只有行内,列信息指的是代码的不包含原始代码的列信息

    • 在开发环境 用source-map 那个最完整 或者cheap-module-eval-source-map 那个最快
    • 在生产环境就flase 或者 hidden-source-map 为错误搜集用
    • 如果需要加载模块的source-map 需要使用source-map-loader
    • vue-cli 用的devtool: '#eval-source-map',

    14、代码检测和npm配合

    与npm 配着
    npm 可以运行node__modules里安装的程序
    可以缩短命令
    
    代码检测

    javascript 用eslint 用.eslintrc json 文件配置 eslint-loader enforce: 'pre'
    ts 用TSlint tslint.json 配置 tslint-loader
    css stylelint 用.stylelintrc 配置 StyleLintPlugin 插件来处理

    代码检测会变慢构建速度,可以配置IDE来检测,同时配置git Hook在代码提交时检测

    15、node运行webapck和中间件

    var config = require('../config')
    var compiler = webpack(webpackConfig)
    

    调用compiler的watch可以监听文件变化

    require('webpack-dev-middleware')是express的一个插件

    var app = express()
    var compiler = webpack(webpackConfig)
    
    var devMiddleware = require('webpack-dev-middleware')(compiler, {
      publicPath: webpackConfig.output.publicPath,
      stats: {
        colors: true,
        chunks: false
      }
    })
    

    但是不支持hot模式需要require('webpack-hot-middleware')

    app.use(hotMiddleware)
    

    代理使用

    var proxyMiddleware = require('http-proxy-middleware')
    

    16、webapck构建优化

    1、减小文件的搜索范围
    1. 优化loader配置,利用好include和exclude
    2. 优化resolve.modules 使用绝对路径
    modules: [path.resolve(__dirname, 'node_modules')]
    
    1. alias 指向 x.min.js 文件,但是lodash不适合
    alias: {
            react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')
        },
    
    1. noParse 配置不解析
    noParse: /jquery|chartjs/ //一般是正则 被忽略的文件不应该有import/require/define等
    
    2、优化构建过程
    1. babel-loader 加 cacheDirectory
    2. DllPlugin 官方推荐做法是把不常变动的文件打DLL

    工程就把 react react-dom prop-types classnames mobx mobx-react lodash moment polyfill 等打进来 可以使用 [npm version]_dll.js 用 npm version 的话只要 version 一改变我们会重新打包,比如升级了 react ,我们就会 version +,就会重新打包。

    先创建一个webpack.config.dll.js

    执行webpack --config ./webpack.config.dll.js把需要dll的文件输出到dist/dll

    const webpack = require('webpack');
    const path = require('path');
    
    const {version} = require('./package.json');
    
    module.exports = {
        entry: {
            'react': [
                'react', 'react-dom',
                'prop-types',
                'classnames',
                'lodash', 'moment'
            ]
        },
        output: {
            path: path.join(__dirname, 'dist/dll'),
            filename: `[name].${version}.js`,
            library: 'dll_[name]',
            publicPath: '/dist/dll/'
        },
        plugins: [
            new webpack.DllPlugin({
                path: path.join(__dirname, 'dist/dll/', '[name].manifest.json'),
                name: 'dll_[name]'
            })
        ]
    };
    
    
    

    在webapck配置文件中使用

    new webpack.DllReferencePlugin({
                context: __dirname,
                // 在这里引入 manifest 文件
                manifest: require('./dist/dll/react.manifest.json')
            }),
    //把js插入到html文件中
    new AddAssetHtmlPlugin({
        filepath: require.resolve(`./dist/dll/${getDLLFileName()}`),
        outputPath: 'dll',
        includeSourcemap: false,
        hash: true,
        publicPath: '/dist/dll/'
    })
    

    原理就是提前构建,缓存起来,用window的全局变量来取。

    1. happypack 对 build 的速度大大大提示,可以多线程打包,cache 也让 rebuild 加快
    {
        test: /.(js|jsx)$/,//命中文件
        use: 'happypack/loader?id=js',//指定loader
        include: path.resolve(__dirname, 'src'),//包括
        exclude: path.resolve(__dirname, 'node_modules')//排除
       }, {
        test: /.(css|scss)$/,
        loader: 'happypack/loader?id=css'
    }
    
    new HappyPack({
        id: 'js',
        threadPool: happyThreadPool,
        loaders: [{
            path: 'babel-loader',
            query: {
                cacheDirectory: true
            }
        }]
    }),
    new HappyPack({
        id: 'css',
        threadPool: happyThreadPool,
        loaders: ['style-loader','css-loader', 'sass-loader']
    }),
    

    原理:happypack 的原理是让loader可以多进程去处理文件,css和js,图片和文件支持不好

    1. Devtool 使用 开发用cheap-module-eval-source-map 那个最快

    2. 压缩 UglifyJsParallelPlugin在webpack2.0以后支持并行,就可以弃用

    3. 开启自动刷新(iframe会比inline快一点)和热更新热替换(hot 开启) 优化就是把node_modules 排除

    4. 区分环境

    if (isDev) {
        config.plugins.push(new webpack.NamedModulesPlugin()); //显示模块更新名字
        config.plugins.push(new webpack.HotModuleReplacementPlugin()); //hot更新插件
    
        config.devServer = {
            hot: true,
            contentBase: './',
            historyApiFallback: {
                index: "/build/index.html"
            },
            publicPath: '/build/',
            host: '0.0.0.0'
        };
        config.devtool = 'eval';
    } else {
        config.plugins.push(new webpack.optimize.UglifyJsPlugin({ // 开启压缩
            cache: true,
            parallel: true,
            compress: {
                warning: fasle,// 是否删除一个警告信息fasle删除
                drop_console: true,//删除console
                collapse_vars: true,是否内嵌虽然已定义但是只用到一次的变量。
                reduce_vars: true ,提取多次的变量
            },
            output: {
                comments: false,//是否删除注释,默认是不删除,设置为false,删除所有的注释
                beautify: fasle, //保留空格和制表符  建议false关闭,最紧凑的输出
            }
            
            
            
        }));
    
        config.devtool = '#source-map';
    }
    

    8、压缩css 在css-loader开启minimize选项 和 提取css到单独的文件

    {
        test: /.(css|less)$/,
        loader: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: 'happypack/loader?id=css'
        })
    },
    
    new ExtractTextPlugin('css/[name].[contenthash:8].css')
    

    9、在webapck中接入cdn

    • 在output.publicPath 中设置javascript的地址
    • 在css-loader.publicPath 中设置css文件中导入的资源的地址
    • 在HtmlWebpackPlugin或者WebPlugin.stylePublicPath 设置css单独是文件的资源

    10、Tree Shaking 去除重复代码 ,webapck没有程序流分析,避免不了babel产生的副作用

    11、以模块化来引入

    // 原来的引入方式
     import {debounce} from 'lodash';
    
    //按模块化的引入方式
    import debounce from 'lodash/debounce';
    

    12、使用异步的模块加载
    require.ensure来设置哪些模块需要异步加载,webpack会将它打包到一个独立的chunk中

    $('.bg-input').click(() => {
        console.log('clicked, loading async.js')
    
        require.ensure([], require => {
    
            require('./components/async2').log();
            require('./components/async1').log();
            console.log('loading async.js done');
        });
    });
    

    import(*) 是新的按需加载,在react-router4中不能使用require.ensure要使用getAsyncComponent函数

    component = {
        getAsyncComponent(() => {
            import('./pages/login')
        })
    }
    

    13、Scope Hoisting 作用域提升 webpack3的新功能

    使用ModuleConcatenationPlugin插件来加快JS执行速度
    这是webpack3的新特性(Scope Hoisting),其实是借鉴了Rollup打包工具来的,它将一些有联系的模块,放到一个闭包函数里面去,通过减少闭包函数数量从而加快JS的执行速度

    new webpack.optimize.ModuleConcatenationPlugin({
    
            })
    

    原理:原理其实很简单,分析模块之间的依赖关系,尽可能将被打散的模块合并到一个函数中,前提是不能造成代码冗余,源码必须采用es6语句。

    14、提取公共代码
    使用CommonsChunkPlugin提取公共的模块,可以减少文件体积,也有助于浏览器层的文件缓存,还是比较推荐的,那个是在最后打包的时候用的。

    // 提取公共模块文件
            new webpack.optimize.CommonsChunkPlugin({
                chunks: ['home', 'detail'],
                // 开发环境下需要使用热更新替换,而此时common用chunkhash会出错,可以直接不用hash
                filename: '[name].js' + (isProduction ? '?[chunkhash:8]' : ''),
                name: 'common'
            }),
    
    
    
    
    // 切合公共模块的提取规则,有时后你需要明确指定默认放到公共文件的模块
    // 文件入口配置
        entry: {
            home: './src/js/home',
            detail: './src/js/detail',
            // 提取jquery入公共文件
            common: ['jquery', 'react', 'react-dom']
        },
        
         entry: {index:'./src/index.js',vendor: ['react','react-dom','react-router']},
        
        new webpack.optimize.CommonsChunkPlugin({
              name: "vendor",
              filename: "vendor.js",
          }),
        
    

    15、prepack

    利用抽象语法树(AST)来分析源码,过来问题大大,不建议使用

    const PrepackWebpackPlugin = require('prepack-webapck-plugin').default;
    
    module.exports = {
        plugins: [
            new PrepackWebpackPlugin()
        ]
    
    }
    
    

    16、可视化的输出分析 Analyse webpack-bundle-analyzer

    17、pwa构建

    用 Webpack 构建接入 Service Workers 的离线应用要解决的关键问题在于如何生成上面提到的 sw.js 文件, 并且sw.js文件中的 cacheFileList 变量,代表需要被缓存文件的 URL 列表,需要根据输出文件列表所对应的 URL 来决定,而不是像上面那样写成静态值。

    假如构建输出的文件目录结构为:

    ├── app_4c3e186f.js
    ├── app_7cc98ad0.css
    └── index.html
    

    那么 sw.js 文件中 cacheFileList 的值应该是:

    var cacheFileList = [
      '/index.html',
      'app_4c3e186f.js',
      'app_7cc98ad0.css'
    ];
    

    Webpack 没有原生功能能完成以上要求,幸好庞大的社区中已经有人为我们做好了一个插件 serviceworker-webpack-plugin 可以方便的解决以上问题。 使用该插件后的 Webpack 配置如下:

    const path = require('path');
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    const { WebPlugin } = require('web-webpack-plugin');
    const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
    
    module.exports = {
      entry: {
        app: './main.js'// Chunk app 的 JS 执行入口文件
      },
      output: {
        filename: '[name].js',
        publicPath: '',
      },
      module: {
        rules: [
          {
            test: /.css/,// 增加对 CSS 文件的支持
            // 提取出 Chunk 中的 CSS 代码到单独的文件中
            use: ExtractTextPlugin.extract({
              use: ['css-loader'] // 压缩 CSS 代码
            }),
          },
        ]
      },
      plugins: [
        // 一个 WebPlugin 对应一个 HTML 文件
        new WebPlugin({
          template: './template.html', // HTML 模版文件所在的文件路径
          filename: 'index.html' // 输出的 HTML 的文件名称
        }),
        new ExtractTextPlugin({
          filename: `[name].css`,// 给输出的 CSS 文件名称加上 Hash 值
        }),
        new ServiceWorkerWebpackPlugin({
          // 自定义的 sw.js 文件所在路径
          // ServiceWorkerWebpackPlugin 会把文件列表注入到生成的 sw.js 中
          entry: path.join(__dirname, 'sw.js'),
        }),
      ],
      devServer: {
        // Service Workers 依赖 HTTPS,使用 DevServer 提供的 HTTPS 功能。
        https: true,
      }
    };
    

    18、多页

    AutoWebPlugin插件来配置多页
    ├── pages
    │   ├── index
    │   │   ├── index.css // 该页面单独需要的 CSS 样式
    │   │   └── index.js // 该页面的入口文件
    │   └── login
    │       ├── index.css
    │       └── index.js
    ├── common.css // 所有页面都需要的公共 CSS 样式
    ├── google_analytics.js
    ├── template.html
    └── webpack.config.js
    
    
    • 所有单页应用的代码都需要放到一个目录下,例如都放在 pages 目录下;
    • 一个单页应用一个单独的文件夹,例如最后生成的 index.html 相关的代码都在 index 目录下,login.html 同理;
    • 每个单页应用的目录下都有一个 index.js 文件作为入口执行文件。

    AutoWebPlugin 强制性的规定了项目部分的目录结构,在pages下,每一个文件夹就是一个目录。通过插件自动生成手动需要配置的两个html插件。

    const { AutoWebPlugin } = require('web-webpack-plugin');
    
    // 使用本文的主角 AutoWebPlugin,自动寻找 pages 目录下的所有目录,把每一个目录看成一个单页应用
    const autoWebPlugin = new AutoWebPlugin('pages', {
      template: './template.html', // HTML 模版文件所在的文件路径
      postEntrys: ['./common.css'],// 所有页面都依赖这份通用的 CSS 样式文件
      // 提取出所有页面公共的代码
      commonsChunk: {
        name: 'common',// 提取出公共代码 Chunk 的名称
      },
    });
    
    module.exports = {
      // AutoWebPlugin 会为寻找到的所有单页应用,生成对应的入口配置,
      // autoWebPlugin.entry 方法可以获取到所有由 autoWebPlugin 生成的入口配置
      entry: autoWebPlugin.entry({
        // 这里可以加入你额外需要的 Chunk 入口
      }),
      plugins: [
        autoWebPlugin,
      ],
    };
    
    

    template.html 模版文件如下:

    <html>
    <head>
      <meta charset="UTF-8">
      <!--在这注入该页面所依赖但没有手动导入的 CSS-->
      <!--STYLE-->
      <!--注入 google_analytics 中的 JS 代码-->
      <script src="./google_analytics.js?_inline"></script>
      <!--异步加载 Disqus 评论-->
      <script src="https://dive-into-webpack.disqus.com/embed.js" async></script>
    </head>
    <body>
    <div id="app"></div>
    <!--在这注入该页面所依赖但没有手动导入的 JavaScript-->
    <!--SCRIPT-->
    <!--Disqus 评论容器-->
    <div id="disqus_thread"></div>
    </body>
    </html>
    
    在模版中生成
    • CSS 类型的文件注入到 所在的位置,如果 不存在就注入到 HTML HEAD 标签的最后;
    • JavaScrip 类型的文件注入到 所在的位置,如果 不存在就注入到 HTML BODY 标签的最后。
    通过gulp来管理html,webapck来处理js也是解决多页的好方法
    利用html-webpack-plugin 插件循环生成
    // 引入插件
    const HTMLWebpackPlugin = require("html-webpack-plugin");
    // 引入多页面文件列表
    const { HTMLDirs } = require("./config");
    // 通过 html-webpack-plugin 生成的 HTML 集合
    let HTMLPlugins = [];
    // 入口文件集合
    let Entries = {}
    
    // 生成多页面的集合
    HTMLDirs.forEach((page) => {
        const htmlPlugin = new HTMLWebpackPlugin({
            filename: `${page}.html`,
            template: path.resolve(__dirname, `../app/html/${page}.html`),
            chunks: [page, 'commons'],
        });
        HTMLPlugins.push(htmlPlugin);
        Entries[page] = path.resolve(__dirname, `../app/js/${page}.js`);
    })
    

    19、同构

    构建服务端渲染

    服务端渲染的代码要运行在nodejs环境,和浏览器不同的是,服务端渲染代码需要采用commonjs规范同时不应该包含除js之外的文件比如css。webpack配置如下:

    module.exports = {
      target: 'node',
      entry: {
        'server_render': './src/server_render',
      },
      output: {
        filename: './dist/server/[name].js',
        libraryTarget: 'commonjs2',
      },
      module: {
        rules: [
          {
            test: /.js$/,
            loader: 'babel-loader',
          },
          {
            test: /.(scss|css|pdf)$/,
            loader: 'ignore-loader',
          },
        ]
      },
    };
    

    其中几个关键的地方在于:

    target: 'node' 指明构建出的代码是要运行在node环境里
    libraryTarget: 'commonjs2' 指明输出的代码要是commonjs规范
    {test: /.(scss|css|pdf)$/,loader: 'ignore-loader'} 是为了防止不能在node里执行服务端渲染也用不上的文件被打包进去。

    20、laoder与plugins插件编写

    如果你的扩展是想对一个个单独的文件进行转换那么就编写loader剩下的都是plugin

    其中对文件进行转换可以是像:

    • babel-loader把es6转换成es5
    • file-loader把文件替换成对应的URL
    • raw-loader注入文本文件内容到代码里去

    编写loader非常简单,以comment-require-loader为例:

    module.exports = function (content) {
        return replace(content);
    };
    

    loader的入口需要导出一个函数,这个函数要干的事情就是转换一个文件的内容。
    函数接收的参数content是一个文件在转换前的字符串形式内容,需要返回一个新的字符串形式内容作为转换后的结果,所有通过模块化倒入的文件都会经过loader。从这里可以看出loader只能处理一个个单独的文件而不能处理代码块

    class EndWebpackPlugin {

    constructor(doneCallback, failCallback) {
        this.doneCallback = doneCallback;
        this.failCallback = failCallback;
    }
    
    apply(compiler) { 汇编者; 编辑者; 编纂者 
        // 监听webpack生命周期里的事件,做相应的处理
        compiler.plugin('done', (stats) => {
            this.doneCallback(stats);
        });
        compiler.plugin('failed', (err) => {
            this.failCallback(err);
        });
    }
    

    }

    module.exports = EndWebpackPlugin;
    loader的入口需要导出一个class, 在new EndWebpackPlugin()的时候通过构造函数传入这个插件需要的参数,在webpack启动的时候会先实例化plugin再调用plugin的apply方法,插件需要在apply函数里监听webpack生命周期里的事件,做相应的处理。
    webpack plugin 里有2个核心概念:

    Compiler: 从webpack启动到推出只存在一个Compiler,Compiler存放着webpack配置
    Compilation: 由于webpack的监听文件变化自动编译机制,Compilation代表一次编译。
    Compiler 和 Compilation 都会广播一系列事件。
    webpack生命周期里有非常多的事件可以在event-hooks和Compilation里查到

    21、常用loader与plugins

    加载文件
    • raw-loader:把文本文件的内容加载到代码中去,在 3-20加载SVG 中有介绍。
    • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件,在 3-19加载图片、3-20加载 SVG、4-9 CDN 加速 中有介绍。
    • url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去,在 3-19加载图片、3-20加载 SVG 中有介绍。
    • source-map-loader:加载额外的 Source Map 文件,以方便断点调试,在 3-21加载 Source Map 中有介绍。
    • svg-inline-loader:把压缩后的 SVG 内容注入到代码中,在 3-20加载 SVG 中有介绍。
    • node-loader:加载 Node.js 原生模块 .node 文件。
    • image-loader:加载并且压缩图片文件。
    • json-loader:加载 JSON 文件。
    • yaml-loader:加载 YAML 文件。
    编译模版
    • pug-loader:把 Pug 模版转换成 JavaScript 函数返回。
    • handlebars-loader:把 Handlebars 模版编译成函数返回。
    • ejs-loader:把 EJS 模版编译成函数返回。
    • haml-loader:把 HAML 代码转换成 HTML。
    • markdown-loader:把 Markdown 文件转换成 HTML。
    转换脚本语言
    • babel-loader:把 ES6 转换成 ES5,在3-1使用 ES6 语言中有介绍。
    • ts-loader:把 TypeScript 转换成 JavaScript,在3-2使用 TypeScript 语言中有遇到。
    • awesome-typescript-loader:把 TypeScript 转换成 JavaScript,性能要比 ts-loader 好。
    • coffee-loader:把 CoffeeScript 转换成 JavaScript。
    转换样式文件
    • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性。
    • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
    • sass-loader:把 SCSS/SASS 代码转换成 CSS,在3-4使用 SCSS 语言中有介绍。
    • postcss-loader:扩展 CSS 语法,使用下一代 CSS,在3-5使用 PostCSS中有介绍。
    • less-loader:把 Less 代码转换成 CSS 代码。
    • stylus-loader:把 Stylus 代码转换成 CSS 代码。
    检查代码
    • eslint-loader:通过 ESLint 检查 JavaScript 代码,在 3-16检查代码中有介绍。
    • tslint-loader:通过 TSLint 检查 TypeScript 代码。
    • mocha-loader:加载 Mocha 测试用例代码。
    • coverjs-loader:计算测试覆盖率。
    其它
    • vue-loader:加载 Vue.js 单文件组件,在3-7使用 Vue 框架中有介绍。
    • i18n-loader:加载多语言版本,支持国际化。
    • ignore-loader:忽略掉部分文件,在3-11构建同构应用中有介绍。
    • ui-component-loader:按需加载 UI 组件库,例如在使用 antd UI 组件库时,不会因为只用到了 Button 组件而打包进所有的组件。
    用于修改行为
    • define-plugin:定义环境变量,在4-7区分环境中有介绍。
    • context-replacement-plugin:修改 require 语句在寻找文件时的默认行为。
    • ignore-plugin:用于忽略部分文件。
    用于优化
    • commons-chunk-plugin:提取公共代码,在4-11提取公共代码中有介绍。
    • extract-text-webpack-plugin:提取 JavaScript 中的 CSS 代码到单独的文件中,在1-5使用 Plugin 中有介绍。
    • prepack-webpack-plugin:通过 Facebook 的 Prepack 优化输出的 JavaScript 代码性能,在 4-13使用 Prepack 中有介绍。
    • uglifyjs-webpack-plugin:通过 UglifyES 压缩 ES6 代码,在 4-8压缩代码中有介绍。
    • webpack-parallel-uglify-plugin:多进程执行 UglifyJS 代码压缩,提升构建速度。
    • imagemin-webpack-plugin:压缩图片文件。
    • webpack-spritesmith:用插件制作雪碧图。
    • ModuleConcatenationPlugin:开启 Webpack Scope Hoisting 功能,在4-14开启 ScopeHoisting中有介绍。
    • dll-plugin:借鉴 DDL 的思想大幅度提升构建速度,在4-2使用 DllPlugin中有介绍。
    • hot-module-replacement-plugin:开启模块热替换功能。
    其它
    • serviceworker-webpack-plugin:给网页应用增加离线缓存功能,在3-14 构建离线应用中有介绍。
    • stylelint-webpack-plugin:集成 stylelint 到项目中,在3-16检查代码中有介绍。
    • i18n-webpack-plugin:给你的网页支持国际化。
    • provide-plugin:从环境中提供的全局变量中加载模块,而不用导入对应的文件。
    • web-webpack-plugin:方便的为单页应用输出 HTML,比 html-webpack-plugin 好用。

    22 webpack4

    • webpack 4 更快(速度提升98%)
    • 新增Mode选项,区分环境
    • 零配置以及默认值
    • 再见 CommonsChunkPlugin =》 新 API optimize.splitChunks
    • WebAssembly 支持
    • .mjs 支持
  • 相关阅读:
    软件系统架构演变
    PL/SQL语法
    ambari安装指南
    手把手带你使用JS-SDK自定义微信分享效果
    微信公众平台无法使用支付宝收付款的解决方案
    php创建含文件夹的zip文件
    捣鼓TinyMCE 粘贴图片并上传+Django后台
    捣鼓TinyMCE粘贴图片并上传+Flask后台
    关于Java 中Integer 和Long对象 对比的陷阱(简单却容易犯的错误)
    Java东西太多,记录一些知识点
  • 原文地址:https://www.cnblogs.com/chenjinxinlove/p/8467478.html
Copyright © 2020-2023  润新知