• Webpack从入门到上线


    webpack是目前一个很热门的前端打包工具,官网说得很清楚,webpack的出现就是要把requirejs干掉。同时它还提供了十分便利的本地开发的环境。网上并不容易找到一个讲解得比较详细完整的教程,本文结合实践经验,总结一套可用的开发和上线的配置和流程。

    首先,Require JS有什么问题

    RequireJs存在的问题

    博主先是使用了RequireJs,后来又转了webpack,综合比较,requirejs确实存在一些缺点:

    1.写法比较笨拙

     需要把所有的依赖模块写在require函数里面,当模块很多的时候,看起来逼格就不高了,感受如下: 

    而webpack既兼容requirejs的写法,也兼容commonjs的写法,也就是说,使用webpack你既可以继续像上面那样写,也可以像node那样写,感受如下:

    复制代码
    var modules = {
            signHandler: require("module/sign-log"),
            chatHandler: require("module/chat-win"),
            mapHandler: require("lib/map"),
            util: require("lib/util")
        };
    复制代码

    可以在需要的时候再去require,而不是搞个大括号把全部的模块一下子写到一起。(模块的导出用module.exports = ....)

    当然这两种写法不仅是感光上的区别,逻辑上也有区别。用中括号加载的模块通常webpack是动态去加载,而没有中括号是和主文件打包在一起的。

    2. 没有通用模块的概念

    例如有一个弹框模块,用在登陆注册,并且所有页面都有登陆注册,所以这是一个所有页面的通用模块。如果页面的其它模块都没调到通用模块里面的东西的话,用RequireJs没什么问题。但是实际情况上不是这样的,例如util模块既会被登陆注册的模块调用,也会被很多其它模块调用。这个时候合并压缩就有问题了:合并后的通用模块如common-app.js会带上util的代码、另外一个页面的例如detail.js也会带上util的代码,以后一改util.js里面的东西,就会一并改动其它所有用到util的页面js,就得重新打所有js的版本号。这样无论对布署上线,还是对于用户的缓存来说都是不利的。

     webpack可以把几个文件的通用模块抽出来单独作为一个模块common-chunk.js,引用的时候每个页面先引一个common-chunk.js,再引一个该页面自己的js文件如detail.js,原detail.js里面和其它js文件共用的模块已经被提取到common-chunk.js里面。

    3. 没办法直接动态合并压缩一个需要异步加载的模块

    这个问题是这样的,假设我的聊天模块文件有500Kb这么大,并不希望一刷页面就加载,而是用户点了聊天再去加载。这个聊天模块有一个入口文件和其它几个模块文件,我合并压缩了入口文件,需要有一个输出文件,而入口文件define的模块名和压缩优化后的输出文件的路径肯定是要不一样的,但是压缩之后他并不会自动去改变输出文件的模块名。这样就导致你要手动去改一下压缩文件的模块名,不然会require不到。我之前找了一下,没有找到解决方案,所以采取了一个压缩两次的比较笨拙的方法。

    而webpack有一个文件束chunkFile的概念,它会自动去把需要异步加载的文件变成一个chunkFile,然后触发加载的时候再去加载chunkFile。

    4. 需要借助gulp等管理工具进行开发

    webpack本身有一些插件和第三方的插件,可以在本地开一个webpack-dev-server,文件一保存的时候就会自动打包编译js/css/less/sass等。

     

    使用RequireJs虽然看起来缺点比较多,但是使用RequireJs也有webpack不具备的优点,那就是RequireJs开发的时候在浏览器里面,每个模块都是单独一个文件,跟本地文件保持一致,而webpack是把主文件和该文件都用到的模块都打包成了一个文件,这样在调试的时候就需要你去搜索找到要调试的位置,而使用requireJs直接根据第几行就可以了。不过,考虑到使用webpack可以搭建一个很方便的本地开发环境,所以这个缺点也不是很明显。

    使用webpack

    用一句概括就是:写一个配置文件,然后执行下webpack,就可以把生成的文件输出,可压缩带版本号,同时生成一个source-map文件,这个文件包含了每个模块的js和css的实际(带版本号)路径,根据这个路径就可以把html里面的js/css等换成真实的路径。

    webpack是一个打包的工具,它有一个重要的概念,就是把js/css/image/coffee都当成地位相等的资源,你可以在js里面require一个css,也可以require一个image。但是这种模式比较适用于React等框架,都是用js控制。

     webpack的其它几个重要概念:

    1. loader加载器

    上面说到,各种各样的资源都可以在webpack里面加载,而这些资源都需要相应的加载器,webpack才能识别,然后解析成正常的浏览器认识的资源。

    换句话说,你可以给webpack加载各种各样的资源:css/less/sass/png/babel等,然后在代码里面进行管理。

    例如要加一个sass的loader,需要先安装:

    npm install sass-loader node-sass

    然后在配置文件添加一个loader:

    复制代码
    {
        test: /.sass$/,
        loaders: ["style", "css", "sass"]
    },
    复制代码

    这样当你require(“hello.sass”)的时候,webpack就能处理这种.sass结尾的文件。这样子有两个好处,一个是webpack能够自动编译sass为css,另一个是require进来的style,webpack会把它解析成一个object,这个object的key就是类名,就可以在js使用样式的类名,这种比较适合类似于react的开发模式。

    2. 文件束chunk

    上面提到的,会把动态加载的文件生成一个个的chunk,在配置文件的output里面加一行:

    chunkFilename: "bundle-[id].js"

    就会根据id区分不同动态加载的chunk文件,而这些chunk文件名对于我们来说是无关紧要,因为这个是webpack管理的,开发者无需关心叫什么又是怎么加载的。

    3. webpack-dev-server

      这是webpack的一个插件,可以在本地开一个静态服务,用来作为本地开发的重要工具。具体步骤就是html里面引用的资源用一个假的域名,如develop.com:

    <script src="//develop.com/site/app-init.js"></script>

    然后再把develop.com绑到本地回路:

         127.0.0.1 fedren.com

    这样请求就打到了本地的80端口。同时在本地开一个nginx监听在80端口,nginx收到80端口的请求后,再把请求转发到webpack的服务(默认是8080端口)。这样就能够实现本地开发,下文会具体介绍。

     

    下面一步步介绍怎么配置和使用webpack

    webpack的基本配置

    首先,npm init创建一个node的配置文件package.json,然后安装webpack:

    npm install webpack
    sudo npm install webpack -g //安装一个全局的命令

    再创建一个webpack.config.js文件,加入最基本的配置:

    复制代码
    module.exports = {
        // The standard entry point and output config
        //每个页面的js文件
        entry: {
            home: "js/home",
            detail: "js/detail"
        },
        output: {
            path: "assets",                   //打包输出目录
            publicPath: "/static/build/",     //webpack-dev-server访问的路径
            filename: "[name].js",            //输出文件名
            chunkFilename: "bundle-[id].js"   //输出chunk文件名
        }
    };
    复制代码

    工程的js都放到js目录下,一个叫home.js,另一个叫detail.js,输出到assets目录,publicPath是为webpack-dev-server所使用

    然后在当前目录执行webpack,发现webpack报错了:

    ERROR in Entry module not found: Error: Cannot resolve module 'js/home' in /Users/yincheng/code/blog-webpack

    找不到js/home的模块,只要在配置里面加一句resolve:

        resolve: {
            modulesDirectories: ['.']
        }

    告诉webpack所有模块的启始目录由当前目录开始,再执行下webpack就可以正常输出了:

    到目前为此,当前工程的目录结构就是这样的了:

    接下来,创建html:home.html,里面引入js文件,"static/build"即为上面定义的publicPath:

    复制代码
    <body>
        <p>home.html</p>
        <script src="//develop.com/static/build/home.js"></script>
    </body>
    复制代码

    注意我们用了一个develop.com的域名,把这个域名绑到本地回路:

    127.0.0.1 develop.com

    然后配置nginx,打开nginx.conf,加多一个server:

    复制代码
        server {
            listen       80;
            server_name  payment-admin.com;
            charset utf-8;
            #工程路径
            root    /Users/yincheng/code/demo;
            autoindex       on;
            autoindex_exact_size    on;
    
            location ~* /.+.[a-z]+$ {
                proxy_set_header x-request-filename $request_filename;
                # webpack的服务
                proxy_pass http://127.0.0.1:8080;
            }
         }
    复制代码

    启动nginx或者重启下nginx

    然后再装一个webpack-dev-server:

    npm install webpack-dev-server --save-dev
    sudo npm install webpack-dev-server -g

    然后启动webpack-dev-server,执行:

    webpack-dev-sever --port=8080 //不加port参数,默认就为8080端口

    然后就可以访问:http://develop.com/html/home.html

    这个时候,只要一改变home.js的内容,webpack-dev-server就会自动打包新的文件 ,一刷新页面,就是最新的修改了。这样就实现了最基本的本地开发,不管你用的jsp/php,都不需要把js/css往服务器上传。 注意webpack-dev-server是在内存生成的文件,你在本地是找不到static/build目录的,只有执行了webpack打包才会输出文件到assets目录。一个为上面配置里的publicPath,另一个为path。

     

    引入样式文件——首先创建css/home.css:

    body{
        color: #f00;
    }

    然后在js里面引入这个css文件:

    require("css/home.css");

    一保存之后,会发现webpack-dev-server报错了:

    复制代码
    ERROR in ./css/home.css
    Module parse failed: /Users/yincheng/code/blog-webpack/css/home.css Unexpected token (1:4)
    You may need an appropriate loader to handle this file type.
    复制代码

    根据提示,我们需要加装一个css loader,让webpack能够处理css文件,更改webpack.config.js,加入一个loader:

    复制代码
    module.exports = {
        entry: ...,
        output: ...,
        resolve: ...,
        module: {
            loaders: [
                {
                    test: /.css$/,
                    loader: "style-loader!css-loader"
                },
            ]
        }
    };
    复制代码

    当然要先安装一下:npm install style-loader css-loader --save-dev,然后再重启下webpack-dev-server,就可以加载样式了,我们发现webpack是把样式动态插到了head标签的style里面,但是一般并不希望直接写到head里面,而是独立的一个css文件,这个时候借助一个分离css的插件就可以了:

    npm install extract-text-webpack-plugin --save-dev

    同时把配置文件的loader改一下:

    复制代码
    var ExtractTextPlugin = require("extract-text-webpack-plugin");
    module.exports = {
        module: {
            loaders: [
                // Extract css files
                {
                    test: /.css$/,
                    loader: ExtractTextPlugin.extract("style-loader", "css-loader")
                },
            ]
        },
    
        plugins: [
            new ExtractTextPlugin("[name].css")
        ]
    };
    复制代码

    就会生成和js相同路径和名字的css文件,在home.html里面引入css文件:

    <link rel="stylesheet" href="//develop.com/static/build/home.css"></link> 

    你也可以加载各种各样的loader,如加载一个sass/less loader,require一个sass/less文件后就可以写sass/less了,webpack会把它编译成和上面一样普通的css文件,读者可以自己试试,还可以再装一个png/jpg的loader,指定一个小于多少个k的图片的参数,webpack就会把小于指定尺寸的图片转成base64的格式。各种loader的安装查一查就有了。

     到这里一个最基本的本地开发环境就已经搭起来了。接下来讨论自动刷新

    自动刷新

    上面一保存js/css的时候,webpack server就会自动打包,刷新页面的时候就是最新的修改。这个刷新只要使用webpack的hot模式就可以自动实现,即一保存就自动打包刷新。将上面运行webpack-dev-server的命令再加多两个参数,按照官方文档的方式:

    webpack-dev-server --port=8383 --hot --inline

    如果没有意外,在你的电脑上将会报错:

    复制代码
    ERROR in multi home
    Module not found: Error: Cannot resolve module 'webpack/hot/dev-server' in /Users/yincheng/code/blog-webpack
     @ multi home
    复制代码

    这个问题困惑了笔者好久,因为在node_modules里面是有这个"webpack/hot/dev-server"的,其实只要认真看下上面的提示,就会发现它并不是说在node_modules里面,而是在当前工程目录里,所以把node_modules里的webpack文件夹拷一份到外面就可以正常运行了。(如果你又配了个context的参数的话,那就根据提示拷到context指定的目录)

    使用hot模式,只要一保存js/css就可以自动刷新了,这个功能确实很方便。如果不写参数,也可以把它写在配置文件里面:

    复制代码
    var hotModuleReplacementPlugin = require("webpack/lib/HotModuleReplacementPlugin");
    module.exports = {
        plugins: [
            new ExtractTextPlugin("[name].css"),
            new hotModuleReplacementPlugin()
        ],
        devServer: {
            historyApiFallback: true,
            hot: true,
            inline: true,
            progress: true
        }
    };
    复制代码

    然后运行server就不用带上后面那两个参数了。

    Common chunk

    如上文提到,webpack可以将几个js的公共模块提取成一个chunk,需要借助一个commonChunkPlugin,在上面的plugins再添加一个:

    复制代码
    var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
    plugins: [
            new CommonsChunkPlugin({
                //minChunks: 3,
                name: "common-app.chunk",
                chunks: ["home", "detail", "list"]
            })
        ]
    复制代码

    这样就可以把home、detail、list三个js和css用到的公共模块提取到common-app.chunk.js和common-app.chunk.css这两个文件了。注意页面要先引入这两个文件,然后再引入具体页面的js,webpack在common chunk里面定义了它的require函数。如上面的home.html:

        <script src="//develop.com/static/build/common-app.chunk.js"></script>
        <script src="//develop.com/static/build/home.js"></script>

    可以指定一个minChunk的参数,指定模块至少被require几次才能提取出来,默认是3

    还可以定义两个commonChunk,例如在详情页、列表页和首页都有搜索的模块,而其它页面没有搜索的模块,也就是说除了所有页面都有的公共模块如登陆注册外,还有一个搜索的公共模块有三个页面要用到。如果都用一个common chunk,会把搜索的也放进来,但其它很多页面并不需要用到。这个时候需要加多一个common chunk:

    复制代码
        plugins: [
            new CommonsChunkPlugin({
                name: "search-app.chunk",
                chunks: ["search-app-init", "home", "detail", "list"]
            }),
            new CommonsChunkPlugin({
                name: "common-app.chunk",
                chunks: ["home", "detail", "search-map", "search-app.chunk", "sell", "about", "blog"]
            })
        ]
    复制代码

    注意要把search-app.chunk也写到下面那个所有页面的chunk里面,否则webpack会定义两个一样的require函数,页面的模块也会跟着混乱,一刷页面就报错。页面引用js的顺序就变成了:

        <script src="//develop.com/static/build/common-app.chunk.js"></script>
        <script src="//develop.com/static/build/search-app.chunk.js"></script>
        <script src="//develop.com/static/build/home.js"></script>

    压缩和版本号

    压缩只需要要在plugins里面再添加一个用来压缩的插件:

    复制代码
    var webpack = require("webpack");
    plugins: [
        new webpack.optimize.UglifyJsPlugin()
    ]
    复制代码

    这样执行webpack输出的js/css就是压缩的

    版本号就是在输出带上hash的替换符,如下:

    复制代码
    module.exports = {
        output: {
            path: "assets",
            publicPath: "/static/build/",
            filename: "[name]-[chunkhash].js",
            chunkFilename: "bundle-[chunkhash].js"
        },
    
        plugins: [
            new ExtractTextPlugin("[name]-[contenthash].css")
        ],
    }
    复制代码

    其中js用的是webpack的chunkhash,而css用的是contenthash,contenthash是根据内容生成的hash。如果不用contenthash,那么一改js,css的版本号也会跟着改变,这个就有问题了。webpack还有另外一个自带的叫做"[hash]",这个hash是所有文件都用的同一个哈希,也就是说某个文件改了,所有文件的版本号都会跟着改,所以一般不用这个。

    运行webpack,如果报了下面这个错误:

    复制代码
    ERROR in chunk detail [entry]
    [name]-[chunkhash].js
    Cannot use [chunkhash] for chunk in '[name]-[chunkhash].js' (use [hash] instead)
    复制代码

    那你就把plugins里面的热替换插件注释掉就好了,上线的config不需要热替换:

        plugins: [
            //new hotModuleReplacementPlugin(),
        ],

    成功执行后,就会在设定的output目录下面输出加上版本号的文件:

    复制代码
    .
    ├── detail-d19e4614a1c4f3c1581b.js
    ├── home-11198f8526424e8c58ce10a2799793e3.css
    └── home-5ec13a52eea2a6faf96a.js
    复制代码

    有了版本号之后,下一步是要把html里面的js/css换成带版本号的路径

    替换Html里js/css路径

     之前在html里的路径是test.com,现在要把它换成cdn且带版本号的路径,也就是说,目标是要把下面的引入:

    <script src="//develop.com/static/build/home.js"></script>

    替换成下面的引入,并把新生成的html输出到built目录

    <script src="//cdn.mycdn.com/test/home-5ec13a52eea2a6faf96a.js"></script>

    目测没有现成符合格式的插件可以用,可以自已用node写一个,不费事。

    首先要知道所有文件的对应的版本号,可以用AssetsPlugin,生成source-map:

    复制代码
    var AssetsPlugin = require('assets-webpack-plugin');
        output: {
            publicPath: "//cdn.mycdn.com/static/build/"
        },
        plugins: [
            new AssetsPlugin({filename: './source-map.json', prettyPrint: true}),
        ]
    复制代码

    执行webpack之后,就会生成source-map.json,打开这个文件:

    复制代码
    {
      "detail": {
        "js": "//cdn.mycdn.com/static/build/detail-c8a2c82ebe2e48e06564.js"
      },
      "home": {
        "js": "//cdn.mycdn.com/static/build/home-380af86bfeb6fcb477a4.js",
        "css": "//cdn.mycdn.com/static/build/home-11198f8526424e8c58ce10a2799793e3.css"
      }
    }
    复制代码

    根据develop.com开头的以及最后面的home.js/home.css,就可以在上面找到对应的路径名。笔者写了个脚本,可以实现这个功能,详见:version-control-replace-html

    到这里,整个流程就基本完成了。还有一些优化的步骤

    优化

    1. 优化模块id

     webpack对于每个模块都是用id标志,而不是用模块的名字,只是为了节省空间。还可以再节省,就是用它自带的occurrence-order插件将最常用的模块靠前,这样可以再节省一点点空间,因为id是从0开始排的,从一位数到n位数。

    new webpack.optimize.OccurenceOrderPlugin()

    2. 移出版本号

    在上面用了common-chunk的插件,抽离公共模块,在这个common-chunk.js里,webpack会定义每个模块加载的src,以便于加载那些需要动态加载的chunk,如下:

    script.src = __webpack_require__.p + "" + chunkId + "-" + {"0":"0cb48ff1ab1d1156015d","5":"e9e7f761f306c648ccef","6":"cbbdf8e3ad1aba34ced0"}[chunkId] + ".js";

    从上面可以看出它会把版本号也写在里面,这样就导致一个问题,每改一个js文件,它的版本号就会变化,就会导致common chunk里面的内容发生变化,所以它的版本号也得跟着变,也就是说改了一个文件,影响了两个文件。所以需要把它抽出来,有个插件已经做了这样的事情,叫做ChunkManifestPlugin

    复制代码
    var ChunkManifestPlugin = require('chunk-manifest-webpack-plugin');
    plugins: [
            new ChunkManifestPlugin({
                filename: "chunk-manifest.json",
                manifestVariable: "webpackManifest"
            })
    ]
    复制代码

    传两个参数,一个是输出文件名,另一个是变量名,用于上面的script.src,执行webpack后,它会把上面script.src的那一坨东西放到chunk-manifest.json,然后在页面写一个内联的script,定义一个全局变量window.webpackManifest,值为manifest.json里面的内容。笔者已在上面的替换版本号的脚本做处理,只需在页面合适的地方写上一行:

    <!--%webpack manifest%-->

    就会把这行替换成一个script标签。

    3. 多个common-chunk的优化

     在上面写了两个common chunk,在生成的两个chunk文件里面,你会发现大量的的重复代码,已经失去了公共模块的作用,这个问题可以用一个MoveToParentMergingPlugin解决,它会把search-app用到的common-app的模块全部移到了common-app,search-app就不会重复common-app的内容了。

    html保存自动刷新

     上面提到,只要一保存css/js,webpack-dev-server就会自动保存和刷新,但是html/jsp没办法(如果你用react开发,可以用react-hot-loader),其实可以手动解决这个问题。打开node_modules/webpack-dev-server/client/index.js这个文件,可以发现webpack是用的sockjs实现自动刷新的。浏览器使用sockjs创建socket客户端,连接到webpack的服务,保存更改的时候,服务就向浏览器的socket发送消息,接收到这个消息后客户端就调window.location.reload刷新页面。所以可以模仿这个过程,在本地另开一个服务,监听html的修改,然后向浏览器端发送刷新页面的消息。

    具体来说,首先在上面的node_modules/webpack-dev-server/client/index.js这个文件最后面再添加一个socket连接:

    复制代码
    /*自定义reload window*/
    var reload = new SockJS("http://localhost:9999/reload");
    reload.onopen = function(){
        console.log("customer reload start.......");
    }
    
    reload.onclose = function(){
        console.log("customer reload close.......");
    }
    
    reload.onmessage = function(_msg){
        var msg = JSON.parse(_msg.data);
        if(msg.type === "reload"){
            console.log("customer reload window now");
            window.location.reload();
        }
    }
    复制代码

    这个9999端口的server就是下面要在本地监听的一个socket服务。在开这个socket服务之前,需要先在本地开一个监听文件修改的服务,然后再向这个socket服务发送消息。监听的服务比较好写,有现成的node包可以用:chokidar,使用也非常简单。监听到修改之后就可以执行上传服务器的命令,然后(使用进程间的通信)再向socket服务发送一个需要刷新的消息,再传递给浏览器的scoket,如上面的代码,一收到消息就刷新页面。具体代码查看github 

     除了优化,在使用中会遇到的一些问题:

    解决问题

    1. umd的require模式

     有时候会引入外部的库,这些库可能会用umd的require模式,判断是要用requirejs还是commonjs或是写个全局的函数:

    复制代码
        /* CommonJS */ if (typeof require === 'function' && typeof module === 'object' && module && typeof exports === 'object' && exports)
            module['exports'] = init(require("ByteBuffer"));
        /* AMD */ else if (typeof define === 'function' && define["amd"])
            define("lib/chat/ProtoBuf", ["./ByteBuffer"], init);
        /* Global */ else(global["dcodeIO"] = global["dcodeIO"] || {})["ProtoBuf"] = init(global["dcodeIO"]["ByteBuffer"]);
    复制代码

    这个的问题就在于,只要页面上有require出现,webpack就会去打包,不管你是写if里面还click事件里面。因为像上面说的,webpack会把异步加载的文件打包成一个boundle文件,同时也会把非异步的打包到一起。像上面那样写,它会重复打包,生成好多个bundle。只要加多一个umdREquirePlugin,webpack就能正常打包了。

    2. 如何加载外部资源

     webpack是一个打包的工具,它并不是像requireJs那样可以支持直接require一个外部资源。

    例如我要require谷歌地图:https://maps.googleapis.com/maps/api/js,打包的时候webpack会给出一个warning,说加载不到这个外部资源,运行代码的时候会报错,提示没有这个模块。

    另外一个问题是,我需要if else判断,如果是中国的环境就加载中国域名的谷歌地图:http://ditu.google.cn/maps/api/js 否则就加载上面的,使用webpack是没办法做到的, 使用requireJs就可以很简单地直接require一下就行。

    但其实这个问题很好解决只要自己写一个动态加载script的函数就好了,一个兼容性很好的版本:

    复制代码
    function loadScript(url, callback){
                var script = document.createElement("script")
                script.type = "text/javascript";
                if (script.readyState){  //IE
                    script.onreadystatechange = function(){
                        if (script.readyState == "loaded" || script.readyState == "complete"){
                            script.onreadystatechange = null;
                            callback();
                        }
                    };
                } else {  //Others
                    script.onload = function(){ callback(); };
                }
                script.src = url;
                document.getElementsByTagName("head")[0].appendChild(script);
            }
    复制代码

    详见:The best way to load external JavaScript

    webpack虽然是一个利器,但是坑也不少,目前遇到过的不太好解决的问题:

    遇到的困难

    1. chunkhash

     使用chunkhash有两个问题,一个是css改变之后,js的版本号也会跟着改变,即使js没有修改,但是比较这两个js文件的时候,你会发现这两个版本号不一样的文件内容是完全一模一样的。因为chunkhash不是根据文件内容算的hash值。第二个问题是,相同的代码在不同人的机器上打的包的版本号不一样。如果使用一些根据文件内容打版本号的插件,如webpack-md5-hash,这个插件是用文件内容作一个md5的计算得出一个版本号,这样可以解决上面的两个问题,但是又引发了新的问题,这个md5的时不时就会出现打的版本号不唯一的情况,文件内容不同、版本号相同,而且这个概率还不小。所以最后还是放弃了使用这个插件,然后又尝试了另外一个使用sha算法计算,但是这个改了一个文件会使几个文件的版本号也发生变化。现在还是使用chunkhash

    2. 模块id发生变化

    上文提到,webpack的模块是用id标志的,每个模块对就一个id,例如util对应2,但是这个id不是固定不变的,在n次修改和打包之后,util的id可能会变成了3,这个就比较坑了,给增量上线造成了阻力,即单独上一个html有风险。因为在common-chunk里面,util的id是上次打包的时候定的,但是你这次打包util的id变了,而你只想上home.html,在home.html里面引的home.js里面使用到的util的id对不上common-chunk里面的,导致不能在home里面正常地加载util这个模块。一个临时的解决办法是,home.js不要使用common-chunk,所有的模块都打包到home.js里面就不会有这个问题。

    综上对于webpack的介绍基本说完了,后续会继续研究webpack的打包方式和怎么样写一个webpack的插件。如果上面有什么不合理或可以优化的地方还请指出。

  • 相关阅读:
    QT:浮动的饼状统计图(自绘不规则窗口)
    在QTableView中使用各种自定义委托
    QT:使用“状态模式”绘制界面
    Linux IO控制命令生成
    C++ new和delete实现原理——new和delete最终调用malloc和free
    Qt中如何 编写插件 加载插件 卸载插件
    QT:用QSet储存自定义结构体的问题——QSet和STL的set是有本质区别的,QSet是基于哈希算法的,要求提供自定义==和qHash函数
    两种方法:VS2008下C++窗体程序显示控制台的方法——在QT程序中使用cout和cin
    把自定义控件集成到Qt Designer中
    关闭Windows 2008下面应用程序出错后的提示
  • 原文地址:https://www.cnblogs.com/tangshiguang/p/6746198.html
Copyright © 2020-2023  润新知