Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack 区分环境使用CDN以及HtmlWebpackPlugin插件的编写 #2

Closed
zhouzhili opened this issue Dec 14, 2018 · 0 comments
Closed
Labels

Comments

@zhouzhili
Copy link
Owner

zhouzhili commented Dec 14, 2018

webpack 区分环境使用CDN以及HtmlWebpackPlugin插件的编写

最近项目由1.0版开发到了1.1版,内容越来越多,引入的库也越来越多,每次打包的时候webpack都会提示,包的大小太大什么的,影响性能,作为一个有追求的前端当然不会放任不管,于是,Google一番,引入cdn,在index.html中写入

 <script src="https://unpkg.com/vue@2.5.21/dist/vue.min.js"></script>
 <script src="https://unpkg.com/vuex@3.0.1/dist/vuex.min.js"></script>
 <script src="https://unpkg.com/element-ui@2.4.11/lib/index.js"></script>

在webpack.base.conf.js中添加要忽略的包

 externals: {
    vue: 'Vue',
    vuex: 'Vuex',
    'element-ui': 'ELEMENT'
  }

一切都看起来很不错,but,当我背着电脑回家,打开项目,npm run dev,输入localhost:8080,页面出不来,额,忘了没有联网。。。但是,没有网就不能开发吗,显然不能,于是注释掉上面这些代码,页面终于出来了,这么注释来注释去,不够优雅,我只想在生产环境下使用CDN,在开发环境下还是想用本地的,有了问题我们来解决

区分环境使用CDN

HtmlWebpackPlugin插件是我们开发时基本上必用的一个webpack插件,网上的介绍也很多,它主要是完成对index.html的js,css等资源的注入,同时它也能定义一些变量,template即我们的模板文件如果没有使用loader的时候,默认以ejs格式处理,这样,我们就可以在index.html方便的处理了

  • 首先,去掉index.html中我们添加的CDN引用

  • 去掉webpack.base.conf.js中的externals配置,将其放到webpack.prod.conf.js中

  • 在webpack.prod.conf.js中的HtmlWebpackPlugin中添加一个变量cdn,最终修改为如下所示:

    new HtmlWebpackPlugin({
          filename: config.build.index,
          template: 'index.html',
          inject: true,
          cdn:[
            'https://unpkg.com/vue@2.5.21/dist/vue.min.js',
            'https://unpkg.com/vuex@3.0.1/dist/vuex.min.js',
            'https://unpkg.com/element-ui@2.4.11/lib/index.js'
          ],
          minify: {
            removeComments: true,
            collapseWhitespace: true,
            removeAttributeQuotes: true
          },
          chunksSortMode: 'dependency'
        })
  • 将index.html中添加如下内容:

    <% if(htmlWebpackPlugin.options.cdn){ %> <% for(var i in htmlWebpackPlugin.options.cdn){ %>
        <script src="<%= htmlWebpackPlugin.options.cdn[i] %>"></script>
        <% } %> <% } %>

    使用ejs语法将定义在HtmlWebpackPlugin中的cdn写入,这样就解决了只在生产环境使用CDN,在开发环境下使用本地的。但是,作为一个完美主义者,修改HtmlWebpackPlugin插件的配置以及在index.html中写入一串类似js的代码总觉得不够优雅,难道不能直接在HtmlWebpackPlugin注入的阶段插入CDN吗,于是一顿操作,最终发现HtmlWebpackPlugin提供了事件接口,我们可以在它的基础上开发自己的插件,bingo,那么就来开发一个吧。

    HtmlWebpackPlugin插件的编写

    HtmlWebpackPlugin插件在3.x和4.x版本上的写法是不一样的,我们的开发环境使用的webpack3.x的,因此下面我基于3.x进行开发。

    HtmlWebpackPlugin提供了5个异步事件和1个同步事件,这些在官方GitHub和其他网友的博客中都有写到,我就不再叙述了,我主要使用了html-webpack-plugin-before-html-processing事件,在HTML处理之前将js注入进去,官网给了开发插件的例子:

    function MyPlugin(options) {
      // Configure your plugin with options...
    }
    
    MyPlugin.prototype.apply = function(compiler) {
      // ...
      compiler.plugin('compilation', function(compilation) {
        console.log('The compiler is starting a new compilation...');
    
        compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) {
          htmlPluginData.html += 'The magic footer';
          callback(null, htmlPluginData);
        });
      });
    
    };
    
    module.exports = MyPlugin;

    htmlPluginData我们打印出来一看,属性一目了然:

    {
        html:''//HTML模板内容,
        assets:{
            chunks:[],//入口文件的内容
            js:[],//要插入的js文件的路径  ---就是我们要找的
            css:[],//要插入的css文件的路径
            manifest:''
        },
        ....
    }

    assets.js就是我们要找的,编写插件其实只要一句话

    htmlPluginData.assets.js = this.options.js.concat(htmlPluginData.assets.js)

    so easy,下面是插件的完整代码:

    class InjectCDN {
      constructor (options) {
        const defaultOpt = {
          js: []
        }
        this.options = Object.assign(defaultOpt, options)
      }
    
      // 处理方法
      apply (compiler) {
        // webpack3.x
        compiler.plugin('compilation', compilation => {
          compilation.plugin(
            'html-webpack-plugin-before-html-processing',
            (htmlPluginData, callback) => {
              // 将js添加进去
              htmlPluginData.assets.js = this.options.js.concat(htmlPluginData.assets.js)
              if (callback) {
                return callback(null, htmlPluginData)
              } else {
                return Promise.resolve(htmlPluginData)
              }
            }
          )
        })
      }
    }
    module.exports = InjectCDN

    在webpack.prod.conf.js中这样使用:

    new HtmlWebpackPlugin({}),
    new InjectCDN({
          js: [
            'https://unpkg.com/vue@2.5.21/dist/vue.min.js',
            'https://unpkg.com/vuex@3.0.1/dist/vuex.min.js',
            'https://unpkg.com/element-ui@2.4.11/lib/index.js'
          ]
    })

    new InjectCDN需要放到new HtmlWebpackPlugin后面。

    这样,就完成了在发布的时候使用cdn,在开发的时候使用本地文件。同时,这个插件也可以实现不同环境下不同CDN的切换,方便调试和发布。

@zhouzhili zhouzhili changed the title webpack 在不同环境下使用不同的CDN地址 webpack 区分环境使用CDN以及HtmlWebpackPlugin插件的编写 Dec 15, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant