精华内容
下载资源
问答
  • 本篇文章主要介绍了详解webpack编译多页面vue项目的配置问题,小编觉得挺不错的,现在分享给大家,也给大家做参考。一起跟随小编过来看看吧
  • webpack编译多页面vue项目的配置问题

    千次阅读 2017-07-17 14:59:55
    一般情况下,构建一vue项目的步骤为: 1,安装nodejs环境 2,安装vue-cli cnpm install vue-cli -g 3,构建vue项目 vue init webpack-simple vue-cli-multipage-demo 4, 安装项目依赖包 ...

    一般情况下,构建一个vue项目的步骤为:
    1,安装nodejs环境
    2,安装vue-cli
    cnpm install vue-cli -g

    3,构建vue项目
    vue init webpack-simple vue-cli-multipage-demo

    4, 安装项目依赖包
    cnpm install

    5,在开发环境下运行该项目:
    npm run dev

    通过上面这几步一个简单的vue项目的开发环境基本就搭建起来,接下来的工作就是填代码了。
    最近在做一个前端代码重构的时候发现一个问题,使用这个脚手架构建的项目满足不了我的需求,其实这个需求有一点违背我们vue的初衷的,vue开发的是单页面应用(SPA),这里我需要他实现多页面的效果。什么意思呢?举个例子:我们在网页开发的时候,有时候点击一个连接,浏览器会新代开一个tab页来显示我们的内容,这个时候其实就出现多页面的情况了,新开的这个页面其实已经不属于我们之前的那个页面,(SPA)其实是通过路由的方式,让多个页面在主页面中显示。但是这个时候新开的页面已经脱离主页面了。
    通过vue-cli脚手架构建的项目的webpack配置文件支持单页面的应用开发,他只有一个入口文件。而且最后只会生产一个页面。如何才能满足我的需求,让webpack同时便于多个页面呢,其实还是比较简单的,只需要将webpack稍微改装一下,就完全可以了。
    首先我们需要在build文件下的utils.js文件家中增加一个获取文件夹中文件路径的方法,这个方法将目标文件解析成对象的形式。
    utils.js

    var glob = require("glob");//分析文件夹中文件路径的第三方模块
    exports.getEntry = function(globPath) {
      var entries = {},
        basename, tmp, pathname;
      if (typeof (globPath) != "object") {
        globPath = [globPath]
      }
      globPath.forEach((itemPath) => {
        glob.sync(itemPath).forEach(function (entry) {
          basename = path.basename(entry, path.extname(entry));
          if (entry.split('/').length > 4) {
            tmp = entry.split('/').splice(-3);
            pathname = tmp.splice(0, 1) + '/' + basename; // 正确输出js和html的路径
            entries[pathname] = entry;
          } else {
            entries[basename] = entry;
          }
        });
      });
      return entries;
    }

    然后修改wenpack.base.conf.js文件,修改入口文件,原来的文件是单文件,现在需要将单文件入口改成多文件入口了。
    webpack.dev.conf.js

    var path = require('path')
    var config = require('../config')
    var utils = require('./utils')
    var projectRoot = path.resolve(__dirname, '../')
    var glob = require('glob');
    var entries = utils.getEntry(['./src/module/**/*.js']); // 获得多页面的入口js文件
    
    var env = process.env.NODE_ENV
    // various preprocessor loaders added to vue-loader at the end of this file
    var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
    var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)
    var useCssSourceMap = cssSourceMapDev || cssSourceMapProd
    
    module.exports = {
      entry: entries,//这是通过前面新增的方法获取的文件路径对象
      output: {
        path: config.build.assetsRoot,
        publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
        filename: '[name].js'
      }
      ...
    }

    下面需要修改webpack.dev,conf.js文件了,这里主要是修改原来配置的首页,这里需要配置多个页面
    webpack.dev.conf.js

    var path = require('path');
    var config = require('../config')
    var webpack = require('webpack')
    var merge = require('webpack-merge')
    var utils = require('./utils')
    var baseWebpackConfig = require('./webpack.base.conf')
    var HtmlWebpackPlugin = require('html-webpack-plugin')
    Object.keys(baseWebpackConfig.entry).forEach(function (name) {
      baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
    })
    module.exports = merge(baseWebpackConfig, {
       ...
    });
    //新增
    var pages =utils.getEntry(['./src/module/*.html','./src/module/**/*.html']);
    for (var pathname in pages) {
      // 配置生成的html文件,定义路径等
      var conf = {
        filename: pathname + '.html',
        template: pages[pathname],   // 模板路径
        inject: true,              // js插入位置
        chunksSortMode: 'dependency'
    
      };
    
      if (pathname in module.exports.entry) {
        conf.chunks = ['manifest', 'vendor', pathname];
        conf.hash = true;
      }
    
      module.exports.plugins.push(new HtmlWebpackPlugin(conf));
    }
    

    这里这要是改变了new HtmlWebpackPlugin的conf对象,原来的配置的是单个html,现在通过循环pages对象,生成多个html配置对象。
    通过上面的配置,当我们执行npm run dev 的时候,webpack就可以同时便于多个页面,然后将前面wenpack.base.conf.js中配置的js文件,插入到对应的html页面中。
    这个时候我们运行项目npm run dev 然后我们就可以访问不同的页面了,这里需要注意一下,既然我们需要管理多个页面,我们就应该在src下建立一个目录专门来访放不同的页面。这样项目结构看起来更加清晰,便于维护。
    这是我写的一个demo地址,有兴趣的可以拉下来看看vue-cli-multi-page
    https://github.com/839305939wang/vue-cli-multi-page.git
    运行起来后访问http://localhost:8080/module/index.html,然后点击按钮,打开新页面。
    上面这样配置只是开发环境,最后生产环境的配置文件webpack.prod.conf.js也需要修改,这样在生产打包的时候就可以同时在dist中生成多个html文件。
    webpack.prod.conf:

    var path = require('path')
    var config = require('../config')
    var utils = require('./utils')
    var webpack = require('webpack')
    var merge = require('webpack-merge')
    var baseWebpackConfig = require('./webpack.base.conf')
    var ExtractTextPlugin = require('extract-text-webpack-plugin')
    var HtmlWebpackPlugin = require('html-webpack-plugin')
    var CleanPlugin = require('clean-webpack-plugin')//webpack插件,用于清除目录文件
    var env = config.build.env
    
    var webpackConfig = merge(baseWebpackConfig, {
    ...
    }
    
    //这里是修改的部分,和webpack.dev.conf.js的修改是一样的
    module.exports = webpackConfig
    var pages =utils.getEntry(['./src/module/**/*.html']);
    for (var pathname in pages) {
      // 配置生成的html文件,定义路径等
      var conf = {
        filename: pathname + '.html',//生成 html 文件的文件名
        template: pages[pathname],   // 根据自己的指定的模板文件来生成特定的 html 文件。这里的模板类型可以是任意你喜欢的模板,可以是 html, jade, ejs, hbs, 等等,但是要注意的是,使用自定义的模板文件时,需要提前安装对应的 loader,
        inject: true,              // 注入选项。有四个选项值 true, body, head, false.true:默认值,script标签位于html文件的 body 底部,body:同 true,head:script 标签位于 head 标签内,false:不插入生成的 js 文件,只是单纯的生成一个 html 文件
        // necessary to consistently work with multiple chunks via CommonsChunkPlugin
        chunksSortMode: 'dependency'
      };
      if (pathname in module.exports.entry) {
        conf.chunks = ['manifest', 'vendor', pathname];
        conf.hash = true;
      }
      module.exports.plugins.push(new HtmlWebpackPlugin(conf));
    }

    以上就是使用vue-cli脚手架来解决vue多页面开发的解决方案,
    ‘通假字’较多勿怪!

    展开全文
  • webpack编译流程漫谈

    2020-12-02 18:03:50
    <div><h1>webpack编译流程漫谈 前言 <p>weback在web构建工具的激烈竞争中逐渐脱引而出。 无论是编译速度、报错提示、可扩展性等都给前端开发者耳目一新的感觉。本篇文章是个人对webpack的一点小研究总结。 <h2>...
  • 随着项目不断发展壮大,组件数量开始变得越来越项目也开始变得庞大,webpack编译的时间也会越来越久,我们现在的项目编译一次在40s ——70s之间,这是一效率非常低下的操作。优化的手段有很,之前项目原本...

    本篇文章主要记录下一次 webpack 的性能优化

    现状

    随着项目不断发展壮大,组件数量开始变得越来越多,项目也开始变得庞大,webpack 编译的时间也会越来越久,我们现在的项目编译一次在 40s ——70s 之间,这是一个效率非常低下的操作。优化的手段有很多,之前项目原本已经做了很多,本文从缓存的角度进行优化讲解

    以下仅介绍几种缓存相关的优化手段,包括

    • babel-loader 的 cacheDirectory

    • cache-loader

    • dll 动态链接库

    • HardSourceWebpackPlugin

    先说结论,第一个是项目中已有的,第二第三个效果不大,第四个达到了预期的效果

    我们的 webpack 版本:4.41.2,系统:mac os

    瓶颈分析

    优化的第一步,应该是分析目前的性能,这里我们使用 speed-measure-webpack-plugin 进行速度分析

    // 安装
    npm install --save-dev speed-measure-webpack-plugin
    
    // 使用方式
    const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
     
    const smp = new SpeedMeasurePlugin();
     
    const webpackConfig = smp.wrap({
      plugins: [
        new MyPlugin(),
        new MyOtherPlugin()
      ]
    });
    
    

    结果类似如下,可以看到每一个 Loader 以及 Plugin 的耗时,有了这个,我们就可以“对症下药”

    图片

    但需要注意的是:HardSourceWebpackPlugin 和 speed-measure-webpack-plugin 不能一起使用,这一点让我郁闷了很久

    babel-loader 的 cacheDirectory

    babel-loader 允许使用 Babel 和 webpack 转译 JavaScript 文件,有时候如果我们运行 babel-loader 很慢的话,可以考虑确保转译尽可能少的文件。你可能使用 /\.m?js$/ 来匹配,这样有可能去转译 node_modules 目录或者其他不需要的源代码,导致性能下降

    可以通过 exclude 排除掉一些不需要编译的文件。比如下面就不会去转义 node_modules 和 bower_components 文件夹下面的内容

    module: {
      rules: [
        {
          test: /\.m?js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
              plugins: ['@babel/plugin-proposal-object-rest-spread']
            }
          }
        }
      ]
    }
    

    你也可以通过使用 cacheDirectory 选项,将 babel-loader 提速至少两倍。这会将转译的结果缓存到文件系统中。cacheDirectory 默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程(recompilation process)。如果设置了一个空值 (loader: 'babel-loader?cacheDirectory')或者 true (loader: 'babel-loader?cacheDirectory=true'),loader 将使用默认的缓存目录node_modules/.cache/babel-loader,如果在任何根目录下都没有找到 node_modules 目录,将会降级回退到操作系统默认的临时文件目录。

    {
      test: /\.js$/,
      use: 'babel-loader?cacheDirectory',
      include: [resolve('src'), resolve('test') ,resolve('node_modules/webpack-dev-server/client')]
    }
    

    cache-loader

    除了 babel-loader,如果我们想让其他的 loader 的处理结果也缓存,该怎么做呢?

    答案是可以使用 cache-loader。在一些性能开销较大的 loader 之前添加 cache-loader,以便将结果缓存到磁盘里

    安装

    npm install --save-dev cache-loader
    

    配置

    module.exports = {
      module: {
        rules: [
          {
            test: /\.ext$/,
            use: ['cache-loader', ...loaders],
            include: path.resolve('src'),
          },
        ],
      },
    };
    

    ⚠️ 请注意,保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader 使用此 loader

    除了默认的配置,cache-loader 提供了其他一些选项,详见 cache-loader[1]

    dll 的缓存方案

    什么是 DLL?

    DLL 文件为动态链接库,在一个动态链接库中可以包含给其他模块调用的函数和数据

    为什么要用 DLL?

    原因在于包含大量复用模块的动态链接库只需要编译一次,在之后的构建过程中被动态链接库包含的模块将不会在重新编译,而是直接使用动态链接库中的代码。由于动态链接库中大多数包含的是常用的第三方模块,例如 Vue react、react-dom,只要不升级这些模块的版本,动态链接库就不用重新编译

    如何使用?

    要完成下面三步:

    • 抽离。把网页依赖的基础模块抽离出来,打包到一个个单独的动态链接库中去。一个动态链接库中可以包含多个模块

    • 获取。当需要导入的模块存在于某个动态链接库中时,这个模块不能被再次被打包,而是去动态链接库中获取

    • 加载。页面依赖的所有动态链接库需要被加载

    之前使用 DllPlugin 和 DllReferencePlugin 完成,但是其配置非常复杂,而且假如更新了文件,还需要手动重新生成 dll。这里选择了 AutoDllPlugin[2],它会自动完成以上两个插件的功能,这是 Vue-cli 曾经用过的一个插件

    安装:

    webpack 4

    npm install --save-dev autodll-webpack-plugin
    

    webpack 2 / 3

    npm install --save-dev autodll-webpack-plugin@0.3
    

    基础使用:

    plugins: [
      new HtmlWebpackPlugin({
        inject: true,
        template: './src/index.html',
      }),
      new AutoDllPlugin({
        inject: true, // will inject the DLL bundles to index.html
        filename: '[name].js',
        entry: {
          vendor: [
            'react',
            'react-dom'
          ]
        }
      })
    ]
    

    优化前

    图片

    优化后

    第一次编译:

    图片

    第二次编译:图片

    优化了几s,成效不大

    之所以成效不大,是因为 webpack4 的性能是足够优秀的了,Vue-cli 也废除了这个功能

    HardSourceWebpackPlugin

    安装:

    npm install --save-dev hard-source-webpack-plugin
    # or
    yarn add --dev hard-source-webpack-plugin
    

    配置:

    // webpack.config.js
    var HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
    
    module.exports = {
      context: // ...
      entry: // ...
      output: // ...
      plugins: [
        new HardSourceWebpackPlugin()
      ]
    }
    

    优化前

    图片

    可以看到,需要 50s

    优化后

    第一次启动

    图片

    第二次启动

    图片

    只需要 7 s,减少了 43 s,速度提升百分之八十左右。优化的目的达成!

    热更新速度

    看到 issue 中提到了关于热更新相关的,说是会慢一点,我利用我们的项目做了一些测试,下面是测试数据

    优化前

    js: 2443ms  1634ms 1844ms 2532ms 1443ms 1248ms

    html: 1094ms 1232ms 1119ms 1490ms 1264ms

    css: 1422ms 1186ms 1341ms  1562ms 1183ms

    优化后

    js: 2429ms 2436ms 2860ms 2528ms 1917ms 1487ms 1450ms 1450ms 1557ms 2198ms

    html: 2855ms 1569ms 1400ms 1298ms 1204ms 1299ms 1578ms 1485ms 2028ms

    css: 2035ms 1406ms 1415ms 1600ms 1773ms 1604ms

    相比而言,有时候会慢了一些,但总体而言能够接受。但也有了一些影响,所以项目中提高了两个 npm script 命令,如果不希望开启的话,可以直接 npm run dev:noCache

    "scripts": {
      "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js --cache=true",
      "dev:noCache": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js --cache=false"
    }
    

    在 build/webpack.dev.conf.js 中

    if (args.cache) {
      devConfig = merge(devConfig, {
        plugins: [new HardSourceWebpackPlugin()]
      })
    }
    

    再次强调:

    HardSourceWebpackPlugin 和 speed-measure-webpack-plugin 不能一起使用

    展望未来

    webpack 5 已经发布,其中有一个很吸引人的功能——持久缓存(据说思想跟 HardSourceWebpackPlugin 是一致的)

    通过 cache  缓存生成的 webpack 模块和chunk,来改善构建速度。cache 会在开发模式被设置成 type: 'memory' 而且在生产模式中被禁用

    module.exports = {
      cache: {
        // 1. 将缓存类型设置为文件系统
        type: 'filesystem',
    
        buildDependencies: {
          // 2. 将你的 config 添加为 buildDependency,以便在改变 config 时获得缓存无效
          config: [__filename],
    
          // 3. 如果你有其他的东西被构建依赖,你可以在这里添加它们
          // 注意,webpack、加载器和所有从你的配置中引用的模块都会被自动添加
        },
      },
    };
    展开全文
  • 当你的应用的规模还很小时,你可能不会在乎Webpack编译速度,无论使用3.X还是4.X版本,它都足够快,或者说至少没让你等得不耐烦。但随着业务的增多,嗖嗖嗖一下项目就有上百组件了,也是件很简单的事情。这时候...

    当你的应用的规模还很小时,你可能不会在乎Webpack的编译速度,无论使用3.X还是4.X版本,它都足够快,或者说至少没让你等得不耐烦。但随着业务的增多,嗖嗖嗖一下项目就有上百个组件了,也是件很简单的事情。这时候当你再独立编前端模块的生产包时,或者CI工具中编整个项目的包时,如果Webpackp配置没经过优化,那编译速度都会慢得一塌糊涂。编译耗时10多秒钟的和编译耗时一两分钟的体验是迥然不同的。出于开发时的心情的考虑,加上不能让我们前端的代码编译拖累整个CI的速度这两个出发点,迫使我们必须去加快编译速度。本文主要是探讨下可做编译速度优化的地方,对一些API使用上不会做太多讲解,需要的同学可以直接翻看文档中的介绍。笔者的Webpack版本为4.29.6,后文中内容都基于这个版本。

    一、已存在的针对编译速度的优化

    笔者这套Webpack架子源自CRA的eject,基于Webpack4.x,在Loader和Plugin的选择和设计上已是最佳实践方案,基本上无需改动什么。其原有的对编译的优化配置在于这三处:

    1. 通过terser-webpack-plugin的parallel和cache配置来并行处理并缓存之前的编译结果。terser-webpack-plugin是之前UglifyPlugin的一个替代品,因为UglifyPlugin已经没有继续维护了,从Webpack4.x起,已经推荐使用terser-webpack-plugin来进行代码压缩、混淆,以及Dead Code Elimination以实现Tree Shaking。对于parallel从整个设置的名称大家就会知道它有什么用,没错,就是并行,而cache也就是缓存该插件的处理结果,在下一次的编译中对于内容未改变的文件可以直接复用上一次编译的结果。

    2. 通过babel-loader的cache配置来缓存babel的编译结果。

    3. 通过IgnorePlugin设置对moment的整个locale本地化文件夹导入的正则匹配,来防止将所有的本地化文件进行打包。如果你确实需要某国语言,仅手动导入那国的语言包即可。

    在项目逐渐变大的过程中,生产包的编译时间也从十几秒增长到了一分多钟,这是让人受不了的,这就迫使着笔者必须进行额外的优化以加快编译速度,为编包节省时间。下面的段落就讲解下笔者做的几个额外优化。

    二、多线程(进程)支持

    从上个段落的terser-webpack-plugin的parallel设置中,我们可以得到这个启发:启用多进程来模拟多线程,并行处理资源的编译。于是笔者引入了HappyPack,笔者之前的那套老架子也用了它,但之前没写东西来介绍那套架子,这里就一并说了。关于HappyPack,经常玩Webpack的同学应该不会陌生,网上也有一些关于其原理的介绍文章,也写得很不错。HappyPack的工作原理大致就是在Webpack和Loader之间多加了一层,改成了Webpack并不是直接去和某个Loader进行工作,而是Webpack test到了需要编译的某个类型的资源模块后,将该资源的处理任务交给了HappyPack,再由HappyPack再起内部进行线程调度,分配一个线程调用处理该类型资源的Loader来处理这个资源,完成后上报处理结果,最后HappyPack把处理结果返回给Webpack,最后由Webpack输出到目的路径。将都在一个线程内的工作,分配到了不同的线程中并行处理。

    使用方法如下:

    首先引入HappyPack并创建线程池:

    const HappyPack = require(‘happypack‘);

    const happyThreadPool= HappyPack.ThreadPool({size: require(‘os‘).cpus().length - 1});

    替换之前的Loader为HappyPack的插件:

    {

    test:/\.(js|mjs|jsx|ts|tsx)$/,

    include: paths.appSrc,

    use: [‘happypack/loader?id=babel-application-js‘],

    },

    将原Loader中的配置,移动到对应插件中:

    newHappyPack({

    id:‘babel-application-js‘,

    threadPool: happyThreadPool,

    verbose:true,

    loaders: [

    {

    loader: require.resolve(‘babel-loader‘),

    options: {

    ...省略

    },

    },

    ],

    }),

    大致使用方式如上所示,HappyPack的配置讲解文章有很多,不会配的同学可以自己搜索,本文这里只是顺带说说而已。

    HappyPack老早也没有维护了,它对url-loader的处理是有问题的,会导致经过url-loader处理的图片都无效,笔者之前也去提过一个Issue,有别的开发者也发现过这个问题。总之,用的时候一定要测试一下。

    对于多线程的优势,我们举个例子:

    比如我们有四个任务,命名为A、B、C、D。

    任务A:耗时5秒

    任务B:耗时7秒

    任务C:耗时4秒

    任务D:耗时6秒

    单线程串行处理的总耗时大约在22秒。

    改成多线程并行处理后,总耗时大约在7秒,也就是那个最耗时的任务B的执行时长,仅仅通过配置多线程处理我们就能得到大幅的编译速度提升。

    写到这里,大家是不是觉得编译速度优化就可以到此结束了?哈哈,当然不是,上面这个例子在实际的项目中根本不具有广泛的代表性,笔者实际项目的情况是这样的:

    我们有四个任务,命名为A、B、C、D。

    任务A:耗时5秒

    任务B:耗时60秒

    任务C:耗时4秒

    任务D:耗时6秒

    单线程串行处理的总耗时大约在75秒。

    改成多线程并行处理后,总耗时大约在60秒,从75秒优化到60秒,确实有速度上的提升,但是因为任务B的耗时太长了,导致整个项目的编译速度并没有发生本质上的变化。事实上笔者之前那套Webpack3.X的架子就是因为这个问题导致编译速度慢,所以,只靠引入多线程就想解决大项目编译速度慢的问题是不现实的。

    那我们还有什么办法吗?当然有,我们还是可以从TerserPlugin得到灵感,那就是依靠缓存:在下一次的编译中能够复用上一次的结果而不执行编译永远是最快的。

    至少存在有这三种方式,可以让我们在执行构建时不进行某些文件的编译,从最本质上提升前端项目整体的构建速度:

    1. 类似于terser-webpack-plugin的cache那种方式,这个插件的cache默认生成在node_modules/.cache/terser-plugin文件下,通过SHA或者base64编码之前的文件处理结果,并保存文件映射关系,方便下一次处理文件时可以查看之前同文件(同内容)是否有可用缓存。其他Webpack平台的工具也有类似功能,但缓存方式不一定相同。

    2. 通过externals配置在编译的时候直接忽略掉外部库的依赖,不对它们进行编译,而是在运行的时候,通过

    3. 将某些可以库文件编译以后保存起来,每次编译的时候直接跳过它们,但在最终编译后的代码中能够引用到它们,这就是Webpack DLLPlugin所做的工作,DLL借鉴至Windows动态链接库的概念。

    后面的段落将针对这几种方式做讲解。

    三、Loader的Cache

    除了段落一中提到的terser-webpack-plugin和babel-loader支持cache外,Webpack还直接另外提供了一种可以用来缓存前序Loader处理结果的Loader,它就是cache-loader。通常我们可以将耗时的Loader都通过cache-laoder来缓存编译结果。比如我们打生产环境的包,对于Less文件的缓存你可以这样使用它:

    {

    test:/\.less$/,

    use: [

    {

    loader: MiniCssExtractPlugin.loader,

    options: {

    ...省略

    },

    },

    {

    loader:‘cache-loader‘,

    options: {

    cacheDirectory: paths.appPackCacheCSS,

    }

    },

    {

    loader: require.resolve(‘css-loader‘),

    options: {

    ...省略

    },

    },

    {

    loader: require.resolve(‘postcss-loader‘),

    options: {

    ...省略

    }

    }

    ]

    }

    Loader的执行顺序是从下至上,因此通过上述配置,我们可以通过cache-laoder缓存postcss-loader和css-loader的编译结果。

    但我们不能用cache-loader去缓存mini-css-extract-plugin的结果,因为它的作用是要从前序Loader编译成的含有样式字符串的JS文件中把样式字符串单独抽出来打成独立的CSS文件,而缓存这些独立CSS文件并不是cache-loader的工作。

    但如果是要缓存开发环境的Less编译结果,cache-loader可以缓存style-loader的结果,因为style-loader并没有从JS文件中单独抽出样式代码,只是在编译后的代码中添加了一些额外代码,让编译后的代码在运行时,能够创建包含样式的

    在对样式文件配置cache-loader的时候,一定要记住上述这两点,要不然会出现样式无法正确编译的问题。

    除了对样式文件的编译结果进行缓存外,对其他类型的文件(除了会打包成独立的文件外)的编译结果进行缓存也是可以的。比如url-laoder,只要大小没有达到limitation的图片都会被打成base64,大于limitation的文件会打成单独的图片类文件,就不能被cache-loader缓存了,如果遇到了这种情况,资源请求会404,这是在使用cache-loader时需要注意的。

    当然,通过使用缓存能得到显著编译速度提升的,依旧是那些耗时的Loader,如果对某些类型的文件编译并不耗时,或者说文件本身数量太少,都可以先不必做缓存,因为即便做了缓存,编译速度的提升也不明显。

    最后笔者将所有Loader和Plugin的cache默认目录从node_modules/.cache/移到了项目根目录的build_pack_cache/目录(生产环境)和dev_pack_cache目录(开发环境),通过NODE_ENV自动区分。这么做是因为笔者的CI工程每次会删除之前的node_modules文件夹,并从node_modules.tar.gz解压一个新的node_modules文件夹,所以将缓存放在node_modules/.cache/目录里面会无效,笔者也不想去动CI的代码。通过这个改动,对cache文件的管理更直观一些,也能避免node_modules的体积一直增大。如果想清除缓存,直接删掉对应目录即可。当然了,这两个个目录是不需要被Git跟踪的,所以需要在.gitignore中添加上。CI环境中如果没有对应的缓存目录,相关Loader会自动创建。而且,因为开发环境和生产环境编译出的资源是不同的,在开发环境下对资源的编译往往都没有做压缩和混淆处理等,为了有效地缓存不同环境下的编译结果,需要区分开缓存目录。

    四、外部扩展externals

    按照Webpack官方的说法:我们的项目如果想用一个库,但我们又不想Webpack对它进行编译(因为它的源码很可能已是经过编译和优化的生产包,可以直接使用)。并且我们可能通过window全局方式来访问它,或者通过各种模块化的方式来访问它,那么我们就可以把它配置进extenals里。

    比如我要使用jquery可以这样配置:

    externals: {

    jquery:‘jQuery‘}

    我就可以这样使用了,就像我们直接引入一个在node_modules中的包一样:

    import $ from ‘jquery‘;

    $(‘.div‘).hide();

    这样做能有效的前提就是我们在HTML文件中在上述代码执行以前就已经通过了

    externals还支持其他灵活的配置语法,比如我只想访问库中的某些方法,我们甚至可以把这些方法附加到window对象上:

    externals : {

    subtract : {

    root: ["math", "subtract"]

    }

    }

    我就可以通过 window.math.subtract 来访问subtract方法了。

    对于其他配置方式如果有兴趣的话可以自行查看文档。

    但是,笔者的项目并没有这么做,因为在它最终交付给客户后,应该是处于一个内网环境(或者一个被防火墙严重限制的环境)中,极大可能无法访问任何互联网资源,因此通过

    五、DllPlugin

    在上个段落中的结尾处,提到了笔者的项目在交付用户后会面临的网络困境,所以笔者必须选择另外一个方式来实现类似于externals配置能够提供的功能。那就是Webpack DLLPlugin以及它的好搭档DLLReferencePlugin。笔者有关DLLPlugin的使用都是在构建生产包的时候使用。

    要使用DLLPlugiin,我们需要单独开一个webpack配置,暂且将其命名为webpack.dll.config.js,以便和主Webpack的配置文件webpack.config.js进行区分。内容如下:

    ‘use strict‘;

    process.env.NODE_ENV= ‘production‘;const webpack= require(‘webpack‘);

    const path= require(‘path‘);

    const {dll}= require(‘./dll‘);

    const DllPlugin= require(‘webpack/lib/DllPlugin‘);

    const TerserPlugin= require(‘terser-webpack-plugin‘);

    const getClientEnvironment= require(‘./env‘);

    const paths= require(‘./paths‘);

    const shouldUseSourceMap= process.env.GENERATE_SOURCEMAP !== ‘false‘;

    module.exports= function (webpackEnv = ‘production‘) {

    const isEnvDevelopment= webpackEnv === ‘development‘;

    const isEnvProduction= webpackEnv === ‘production‘;

    const publicPath= isEnvProduction ? paths.servedPath : isEnvDevelopment && ‘/‘;

    const publicUrl= isEnvProduction ? publicPath.slice(0, -1) : isEnvDevelopment && ‘‘;

    const env=getClientEnvironment(publicUrl);return{

    mode: isEnvProduction?

    ‘production‘:

    isEnvDevelopment&& ‘development‘,

    devtool: isEnvProduction?

    ‘source-map‘:

    isEnvDevelopment&& ‘cheap-module-source-map‘,

    entry: dll,

    output: {

    path: isEnvProduction?paths.appBuildDll : undefined,

    filename:‘[name].dll.js‘,

    library:‘[name]_dll_[hash]‘},

    optimization: {

    minimize: isEnvProduction,

    minimizer: [

    ...省略

    ]

    },

    plugins: [newwebpack.DefinePlugin(env.stringified),newDllPlugin({

    context: path.resolve(__dirname),

    path: path.resolve(paths.appBuildDll,‘[name].manifest.json‘),

    name:‘[name]_dll_[hash]‘,

    }),

    ],

    };

    };

    为了方便DLL的管理,我们还单独开了个dll.js文件来管理webpack.dll.config.js的入口entry,我们把所有需要DLLPlugin处理的库都记录在这个文件中:

    const dll ={

    core: [‘react‘,‘@hot-loader/react-dom‘,‘react-router-dom‘,‘prop-types‘,‘antd/lib/badge‘,‘antd/lib/button‘,‘antd/lib/checkbox‘,‘antd/lib/col‘,

    ...省略

    ],

    tool: [‘js-cookie‘,‘crypto-js/md5‘,‘ramda/src/curry‘,‘ramda/src/equals‘,

    ],

    shim: [‘whatwg-fetch‘,‘ric-shim‘],

    widget: [‘cecharts‘,

    ],

    };

    module.exports={

    dll,

    dllNames: Object.keys(dll),

    };

    对于要把哪些库放入DLL中,请根据自己项目的情况来定,对于一些特别大的库,又没法做模块分割和不支持Tree Shaking的,比如Echarts,建议先去官网按项目所需功能定制一套,不要直接使用整个Echarts库,否则会白白消耗许多的下载时间,JS预处理的时间也会增长,减弱首屏性能。

    然后我们在webpack.config.js的plugins配置中加入DLLReferencePlguin来对DLLPlugin处理的库进行映射,好让编译后的代码能够从window对象中找到它们所依赖的库:

    {

    ...省略

    plugins: [

    ...省略//这里的...用于延展开数组,因为我们的DLL有多个,每个单独的DLL输出都需要有一个DLLReferencePlguin与之对应,去获取DLLPlugin输出的manifest.json库映射文件。

    // dev环境下暂不采用DLLPlugin优化。

    ...(isEnvProduction ?dllNames.map(dllName=> newDllReferencePlugin({

    context: path.resolve(__dirname),

    manifest: path.resolve(__dirname,‘..‘, `build/static/dll/${dllName}.manifest.json`)

    })) :

    []

    ),

    ...省略

    ]

    ...

    }

    我们还需要在承载我们应用的index.html模板中加入

    CRA这套架子已经使用了DefinePlugin来在编译时创建全局变量,最常用的就是创建process环境变量,让我们的代码可以分辨是开发还是生产环境,既然已有这样的设计,何不继续使用,让DLLPlugn编译的独立JS文件名暴露在某个全局变量下,并在index.html模板中循环这个变量数组,循环创建

    然后我们改造一下index.html:

    <% if(process.env.NODE_ENV=== "production") {%>

    <%process.env.DLL_NAMES.forEach(function(dllName){%>

    .dll.js">

    Please allow your browser to run JavaScript scripts.
    展开全文
  • 1.创建文件目录先在自己的常用盘中(我自己的项目一般都建在E盘的一文件夹下)创建一文件夹,比如webpack_demo,我用的编辑器是visual studio code,使用ctrl+`直接切到这文件目录下然后在该终端输入npm init 使...

    webpack打包工具现在非常流行,熟悉并且能够进行配置也变得非常重要。在学习和使用的过程中遇到过很多的问题,希望能够让自己记录下来,巩固自己的学习。

    1.创建文件目录

    先在自己的常用盘中(我自己的项目一般都建在E盘的一个文件夹下)创建一个文件夹,比如webpack_demo,我用的编辑器是visual studio code,使用ctrl+`直接切到这个文件目录下

    然后在该终端输入npm init 使其生成一个package.json文件

    然后在webpack_demo文件夹下建立如下文件目录:

    2.进行基础配置打包

    建立好上面的目录之后,现在就该往里面添加内容了。往index.html 、index.js里面添加一些内容,这些内容就看个人随意了,只要是合理的都可以。在这两个文件里面添加内容是为了待会进行webpack打包的时候,不至于让打包出来的东西都是空的,不然这就很尴尬了。然后就该进行webpack.config.js里面配置了。下面是我进行的简单的配置:

    最为简单的入口、出口、插件、本地服务的配置。entry是入口文件,使用的是相对路径,对应到入口文件index.js,这里配置的是单入口文件。多入口的以后配置。output是打包后的文件的路径,使用的是绝对路径。打包后的文件都在dist目录下。出口文件的名称filename使用的是[name].js,这种写法就是为了与入口的文件名称相对应,也有另一个原因,就是多入口文件的时候,出口也相应的有多个,这样使用[name].js就不怕你是单入口还是多入口,很方便。接下来就是本地服务的配置,host可以使用本机ip地址,也可以使用localhost,port就是自己设置的端口。这个html-webpack-plugin插件有两个作用:

    为html文件中引入的外部资源如script、link动态添加每次compile后的hash,防止引用缓存的外部文件问题

    可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口

    这个插件有很多的配置项,这里我只用到了三个,removeAttributeQuotes是否去掉属性的引号,如下type 和 src属性里面的引号就都去掉了;hash 是否为所有注入的静态资源添加webpack每次编译产生的唯一hash值,添加hash形式如下所示

    展开全文
  • webpack 的一次性能优化现状 随着项目不断发展壮大,组件数量开始变得越来越项目也开始变得庞大,webpack 编译的时间也会越来越久,我们现在的项目编译一次在 40s ——70s 之间,这是一效率非常低下的操作。...
  • )之所以提出这个问题, 是因为执行npm run dev 命令后, 控制台打印出了巨量的 v-for 没有key 的warning,导致无法正常的看到编译信息, 因为项目比较大, 文件(这个项目是多人完成的, 很处不规范)给每一个v-for 都...
  • 清理webpack编译时输出的无用信息

    千次阅读 2018-07-04 22:42:50
    最近在使用webpack重新搭建项目,在启动了webpack之后发现在终端输出了很信息,看着很别扭,效果如下: 于是参考了vue-cli的配置,使用FriendlyErrorsWebpackPlugin插件来解决这问题 首先,安装插件 npm ...
  • 于是查找了好多优化webpack编译速度的方案。 优化点 很多个点(的配置)都会有影响。 以下列表中,禁用sourceMap这一条最有效(sourceMap生成花费了编译时间的80%以上)。 但没啥用,开发环境我还是得留着sourceMap方便...
  • 本文中将描述webpack 简述webpack 项目的初始化配置webpack 打包运行一 vue 项目1 什么是webpack,到底需要解决什么问题近几年前端的快速发展,前端已不是简单的html, css, jquery这样一套技术了.前端的世界出现了...
  • 引言 babel是一非常强大的工具,作用远不止我们平时...本文就是运用babel,通过编写babel插件解决了一实际项目中的问题。 本文相关代码已托管至github: babel-plugin-import-customized-require 1. 遇到的问题...
  • 背景我司前端项目框架主要是 vue,多个项目聚集在同一个仓库下,共用公共组件、页面、工具函数等。基于以上前提,我们需要对不同的项目分别进行打包,并解决单页应用强制刷新引起的问题,所以没有使用 vue-cli 来...
  • mpvue一个项目写完了,又来一个项目,一直都使用小程序原生的语法,发现有很的不方便,而且今后的 市场还是以 Vue 居多,所以决定,接下来的项目都用 Vue 来实现。 当然这里想在小程序中实现 Vue 的语法,只能使用...
  • 手上的项目恰好碰到这问题 试了很方法 最后网上找到了解决方案 解决方案: /* autoprefixer: off */ -webkit-box-orient: vertical; /* autoprefixer: on */ 参考链接:...
  • webpack的配置文件并没有固定的命名,也没有固定的路径要求,如果你直接用webpack来执行编译,那么webpack默认读取的将是当前目录下的webpack.config.js 如果你有其它命名的需要或是你有份配置文件(比如你可以...
  • 我司前端项目框架主要是 vue,多个项目聚集在同一个仓库下,共用公共组件、页面、工具函数等。基于以上前提,我们需要对不同的项目分别进行打包,并解决单页应用强制刷新引起的问题,所以没有使用 vue-cli 来创建,...
  • 作者 |SHERlocked93来源 |前端下午茶 授权发布最近在做项目的时候遇到了一个场景:一个项目多个入口,不同的入口,路由、组件、资源等有重叠部分,也有各自不同的部分。由于不同入口下的路由页面有一些是重复的,...
  • 假设您有一个包含多个独立网页的项目。 独立的含义,不共享相同的javascript依赖项。 但是您想要: 使用webpack为每个page生成bundle.js 。 Webpack配置的某些部分将是通用的,但是每个页面的特定配置也是可能的。...
  • 前段时间第一次接触了layabox游戏引擎做了一个项目,由于ES6相当好用,所以在项目中使用了很的ES6语法,但是在发布的时候发现该引擎无法压缩ES6的代码,而不进行压缩的话则代码体积比较大 然后其实该引擎会将自己...
  • 项目是一基于webpack架构的web app脚手架,其特点如下: 更适合页应用。 既可实现全理论分离,也可以生成微观渲染所需要的模板。 约会布局和组件的概念,方便页面间对布局,组件的重用,妈妈再也不用担心我...
  • webpack是一种模块打包工具:它将各种静态资源(比如:JavaScript 文件,图片文件,样式文件等)视为模块,它能够对这些模块进行...3、代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
  • webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个优点: 1.自带模块化(commonjs规范) 2.编译:es6 -> es5 , ...
  • webpack 的一次性能优化现状 随着项目不断发展壮大,组件数量开始变得越来越项目也开始变得庞大,webpack 编译的时间也会越来越久,我们现在的项目编译一次在 40s ——70s 之间,这是一效率非常低下的操作。...
  • 开箱即用的页面模板,基于webpack4 babel7开发可替代的现代化网站,解决非SPA应用,html替换,替代开发编译等问题。 如果兴趣该项目,请点 及时关注项目更新,请点 项目bug,请提 新版本2.x 使用进行构建程序 ...
  • 前端时间做一个项目,不依赖任何框架,如何去实现一个前完全完全分离的页面脚手架呢?思前想后之后,选择了webpack, 用Nodejs的文件读写流,实现了全自动化文件目录编译 用ejs模板,解决了html中头部尾部的抽离 ...
  • 背景介绍 对于大型前端项目,比如公司内部管理系统(一般包括 OA、HR、CRM、会议预约等系统),...preload-routes 和 async-routes 是目前笔者所在团队使用的微前端方案,最终会将整个前端项目拆解成一个主项目多个
  • webpack

    2019-08-13 13:06:53
    webpack处理应用程序时,会递归的构建一个依赖关系图,其中包含应用程序的每个模块,然后将所有这些模块打包成一个或多个bundle。 webpack安装 1.cnpm i -g webpack全局安装webpack,可以全局使用webpack 2.在...

空空如也

空空如也

1 2 3 4 5 ... 18
收藏数 350
精华内容 140
关键字:

webpack编译多个项目