浅谈Webpack 持久化缓存实践

前言

最近在看 webpack 如何做持久化缓存的内容,发现其中还是有一些坑点的,正好有时间就将它们整理总结一下,读完本文你大致能够明白:

  1. 什么是持久化缓存,为什么做持久化缓存?
  2. webpack 如何做持久化缓存?
  3. webpack 做缓存的一些注意点。

持久化缓存

首先我们需要去解释一下,什么是持久化缓存,在现在前后端分离的应用大行其道的背景下,前端 html,css,js 往往是以一种静态资源文件的形式存在于服务器,通过接口来获取数据来展示动态内容。这就涉及到公司如何去部署前端代码的问题,所以就涉及到一个更新部署的问题,是先部署页面,还是先部署资源?

先部署页面,再部署资源:在二者部署的时间间隔内,如果有用户访问页面,就会在新的页面结构中加载旧的资源,并且把这个旧版本资源当做新版本缓存起来,其结果就是:用户访问到一个样式错乱的页面,除非手动去刷新,否则在资源缓存过期之前,页面会一直处于错乱的状态。

先部署资源,再部署页面:在部署时间间隔内,有旧版本的资源本地缓存的用户访问网站,由于请求的页面是旧版本,资源引用没有改变,浏览器将直接使用本地缓存,这样属于正常情况,但没有本地缓存或者缓存过期的用户在访问网站的时候,就会出现旧版本页面加载新版本资源的情况,导致页面执行错误。

所以我们需要一种部署策略来保证在更新我们线上的代码的时候,线上用户也能平滑地过渡并且正确打开我们的网站。

推荐先看这个回答:大公司里怎样开发和部署前端代码?

当你读完上面的回答,大致就会明白,现在比较成熟的持久化缓存方案就是在静态资源的名字后面加 hash 值,因为每次修改文件生成的 hash 值不一样,这样做的好处在于增量式发布文件,避免覆盖掉之前文件从而导致线上的用户访问失效。

因为只要做到每次发布的静态资源(css, js, img)的名称都是独一无二的,那么我就可以:

  1. 针对 html 文件:不开启缓存,把 html 放到自己的服务器上,关闭服务器的缓存,自己的服务器只提供 html 文件和数据接口
  2. 针对静态的 js,css,图片等文件:开启 cdn 和缓存,将静态资源上传到 cdn 服务商,我们可以对资源开启长期缓存,因为每个资源的路径都是独一无二的,所以不会导致资源被覆盖,保证线上用户访问的稳定性。
  3. 每次发布更新的时候,先将静态资源(js, css, img) 传到 cdn 服务上,然后再上传 html 文件,这样既保证了老用户能否正常访问,又能让新用户看到新的页面。

上面大致介绍了下主流的前端持久化缓存方案,那么我们为什么需要做持久化缓存呢?

用户使用浏览器第一次访问我们的站点时,该页面引入了各式各样的静态资源,如果我们能做到持久化缓存的话,可以在 http 响应头加上 Cache-control 或 Expires 字段来设置缓存,浏览器可以将这些资源一一缓存到本地。

用户在后续访问的时候,如果需要再次请求同样的静态资源,且静态资源没有过期,那么浏览器可以直接走本地缓存而不用再通过网络请求资源。

webpack 如何做持久化缓存

上面简单介绍完持久化缓存,下面这个才是重点,那么我们应该如何在 webpack 中进行持久化缓存的呢,我们需要做到以下两点:

  1. 保证 hash 值的唯一性,即为每个打包后的资源生成一个独一无二的 hash 值,只要打包内容不一致,那么 hash 值就不一致。
  2. 保证 hash 值的稳定性,我们需要做到修改某个模块的时候,只有受影响的打包后文件 hash 值改变,与该模块无关的打包文件 hash 值不变。

hash 文件名是实现持久化缓存的第一步,目前 webpack 有两种计算 hash 的方式([hash] 和 [chunkhash])

  1. hash 代表每次 webpack 在编译的过程中会生成唯一的 hash 值,在项目中任何一个文件改动后就会被重新创建,然后 webpack 计算新的 hash 值。
  2. chunkhash 是根据模块计算出来的 hash 值,所以某个文件的改动只会影响它本身的 hash 值,不会影响其他文件。

所以如果你只是单纯地将所有内容打包成同一个文件,那么 hash 就能够满足你了,如果你的项目涉及到拆包,分模块进行加载等等,那么你需要用 chunkhash,来保证每次更新之后只有相关的文件 hash 值发生改变。

所以我们在一份具有持久化缓存的 webpack 配置应该长这样:

module.exports = {
 entry: __dirname + '/src/index.js',
 output: {
 path: __dirname + '/dist',
 filename: '[name].[chunkhash:8].js',
 }
}

上面代码的含义就是:以 index.js 为入口,将所有的代码全部打包成一个文件取名为 index.xxxx.js 并放到 dist 目录下,现在我们可以在每次更新项目的时候做到生成新命名的文件了。

如果是应付简单的场景,这样做就够了,但是在大型多页面应用中,我们往往需要对页面进行性能优化:

  1. 分离业务代码和第三方的代码:之所以将业务代码和第三方代码分离出来,是因为业务代码更新频率高,而第三方代码更新迭代速度慢,所以我们将第三方代码(库,框架)进行抽离,这样可以充分利用浏览器的缓存来加载第三方库。
  2. 按需加载:比如在使用 React-Router 的时候,当用户需要访问到某个路由的时候再去加载对应的组件,那么用户没有必要在一开始的时候就将所有的路由组件下载到本地。
  3. 在多页面应用中,我们往往可以将公共模块进行抽离,比如 header, footer 等等,这样页面在进行跳转的时候这些公共模块因为存在于缓存里,就可以直接进行加载了,而不是再进行网络请求了。

那么如何进行拆包,分模块进行加载,这就需要 webpack 内置插件:CommonsChunkPlugin,下面我将通过一个例子,来诠释 webpack 该如何进行配置。

本文的代码放在我的 Github 上,有兴趣的可以下载来看看:

git clone https://github.com/happylindz/blog.git
cd blog/code/multiple-page-webpack-demo
npm install

阅读下面的内容之前我强烈建议你看下我之前的文章:深入理解 webpack 文件打包机制,理解 webpack 文件的打包的机制有助于你更好地实现持久化缓存。

例子大概是这样描述的:它由两个页面组成 pageA 和 pageB

// src/pageA.js
import componentA from './common/componentA';
// 使用到 jquery 第三方库,需要抽离,避免业务打包文件过大
import $ from 'jquery';
// 加载 css 文件,一部分为公共样式,一部分为独有样式,需要抽离
import './css/common.css'
import './css/pageA.css';
console.log(componentA);
console.log($.trim(' do something '));

// src/pageB.js
// 页面 A 和 B 都用到了公共模块 componentA,需要抽离,避免重复加载
import componentA from './common/componentA';
import componentB from './common/componentB';
import './css/common.css'
import './css/pageB.css';
console.log(componentA);
console.log(componentB);
// 用到异步加载模块 asyncComponent,需要抽离,加载首屏速度
document.getElementById('xxxxx').addEventListener('click', () => {
 import( /* webpackChunkName: "async" */
 './common/asyncComponent.js').then((async) => {
  async();
 })
})
// 公共模块基本长这样
export default "component X";

上面的页面内容基本简单涉及到了我们拆分模块的三种模式:拆分公共库,按需加载和拆分公共模块。那么接下来要来配置 webpack:

const path = require('path');

const webpack = require('webpack');

const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {

 entry: {

 pageA: [path.resolve(__dirname, './src/pageA.js')],

 pageB: path.resolve(__dirname, './src/pageB.js'),

 },

 output: {

 path: path.resolve(__dirname, './dist'),

 filename: 'js/[name].[chunkhash:8].js',

 chunkFilename: 'js/[name].[chunkhash:8].js'

 },

 module: {

 rules: [

  {

  // 用正则去匹配要用该 loader 转换的 CSS 文件

  test: /.css$/,

  use: ExtractTextPlugin.extract({

   fallback: "style-loader",

   use: ["css-loader"]

  }) 

  }

 ]

 },

 plugins: [

 new webpack.optimize.CommonsChunkPlugin({

  name: 'common',

  minChunks: 2,

 }),

 new webpack.optimize.CommonsChunkPlugin({

  name: 'vendor',

  minChunks: ({ resource }) => (

  resource && resource.indexOf('node_modules') >= 0 && resource.match(/.js$/)

  )
 }),
 new ExtractTextPlugin({
  filename: `css/[name].[chunkhash:8].css`,
 }),
 ]
}

第一个 CommonsChunkPlugin 用于抽离公共模块,相当于是说 webpack 大佬,如果你看到某个模块被加载两次即以上,那么请你帮我移到 common chunk 里面,这里 minChunks 为 2,粒度拆解最细,你可以根据自己的实际情况,看选择是用多少次模块才将它们抽离。

第二个 CommonsChunkPlugin 用来提取第三方代码,将它们进行抽离,判断资源是否来自 node_modules,如果是,则说明是第三方模块,那就将它们抽离。相当于是告诉 webpack 大佬,如果你看见某些模块是来自 node_modules 目录的,并且名字是 .js 结尾的话,麻烦把他们都移到 vendor chunk 里去,如果 vendor chunk 不存在的话,就创建一个新的。

这样配置有什么好处,随着业务的增长,我们依赖的第三方库代码很可能会越来越多,如果我们专门配置一个入口来存放第三方代码,这时候我们的 webpack.config.js 就会变成:

// 不利于拓展
module.exports = {
 entry: {
 app: './src/main.js',
 vendor: [
  'vue',
  'axio',
  'vue-router',
  'vuex',
  // more
 ],
 },
}

第三个 ExtractTextPlugin 插件用于将 css 从打包好的 js 文件中抽离,生成独立的 css 文件,想象一下,当你只是修改了下样式,并没有修改页面的功能逻辑,你肯定不希望你的 js 文件 hash 值变化,你肯定是希望 css 和 js 能够相互分开,且互不影响。

运行 webpack 后可以看到打包之后的效果:

├── css

│ ├── common.2beb7387.css

│ ├── pageA.d178426d.css

│ └── pageB.33931188.css

└── js

 ├── async.03f28faf.js

 ├── common.2beb7387.js

 ├── pageA.d178426d.js

 ├── pageB.33931188.js

 └── vendor.22a1d956.js

可以看出 css 和 js 已经分离,并且我们对模块进行了拆分,保证了模块 chunk 的唯一性,当你每次更新代码的时候,会生成不一样的 hash 值。

唯一性有了,那么我们需要保证 hash 值的稳定性,试想下这样的场景,你肯定不希望你修改某部分的代码(模块,css)导致了文件的 hash 值全变了,那么显然是不明智的,那么我们去做到 hash 值变化最小化呢?

换句话说,我们就要找出 webpack 编译中会导致缓存失效的因素,想办法去解决或优化它?

影响 chunkhash 值变化主要由以下四个部分引起的:

  1. 包含模块的源代码
  2. webpack 用于启动运行的 runtime 代码
  3. webpack 生成的模块 moduleid(包括包含模块 id 和被引用的依赖模块 id)
  4. chunkID

这四部分只要有任意部分发生变化,生成的分块文件就不一样了,缓存也就会失效,下面就从四个部分一一介绍:

一、源代码变化:

显然不用多说,缓存必须要刷新,不然就有问题了

二、webpack 启动运行的 runtime 代码:

看过我之前的文章:深入理解 webpack 文件打包机制 就会知道,在 webpack 启动的时候需要执行一些启动代码。

(function(modules) {

 window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {

 // ...

 };

 function __webpack_require__(moduleId) {

 // ...

 }

 __webpack_require__.e = function requireEnsure(chunkId, callback) {

 // ...

 script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"pageA","1":"pageB","3":"vendor"}[chunkId]||chunkId) + "." + {"0":"e72ce7d4","1":"69f6bbe3","2":"9adbbaa0","3":"53fa02a7"}[chunkId] + ".js";

 };

})([]);

大致内容像上面这样,它们是 webpack 的一些启动代码,它们是一些函数,告诉浏览器如何加载 webpack 定义的模块。

其中有一行代码每次更新都会改变的,因为启动代码需要清楚地知道 chunkid 和 chunkhash 值得对应关系,这样在异步加载的时候才能正确地拼接出异步 js 文件的路径。

那么这部分代码最终放在哪个文件呢?因为我们刚才配置的时候最后生成的 common chunk 模块,那么这部分运行时代码会被直接内置在里面,这就导致了,我们每次更新我们业务代码(pageA, pageB, 模块)的时候, common chunkhash 会一直变化,但是这显然不符合我们的设想,因为我们只是要用 common chunk 用来存放公共模块(这里指的是 componentA),那么我 componentA 都没去修改,凭啥 chunkhash 需要变了。

所以我们需要将这部分 runtime 代码抽离成单独文件。

module.exports = {

 // ...

 plugins: [

 // ...

 // 放到其他的 CommonsChunkPlugin 后面

 new webpack.optimize.CommonsChunkPlugin({

  name: 'runtime',

  minChunks: Infinity,
 }),
 ]
}

这相当于是告诉 webpack 帮我把运行时代码抽离,放到单独的文件中。

├── css

│ ├── common.4cc08e4d.css

│ ├── pageA.d178426d.css

│ └── pageB.33931188.css

└── js

 ├── async.03f28faf.js

 ├── common.4cc08e4d.js

 ├── pageA.d178426d.js

 ├── pageB.33931188.js

 ├── runtime.8c79fdcd.js

 └── vendor.cef44292.js

多生成了一个 runtime.xxxx.js,以后你在改动业务代码的时候,common chunk 的 hash 值就不会变了,取而代之的是 runtime chunk hash 值会变,既然这部分代码是动态的,可以通过 chunk-manifest-webpack-plugin 将他们 inline 到 html 中,减少一次网络请求。

三、webpack 生成的模块 moduleid

在 webpack2 中默认加载 OccurrenceOrderPlugin 这个插件,OccurrenceOrderPlugin 插件会按引入次数最多的模块进行排序,引入次数的模块的 moduleId 越小,但是这仍然是不稳定的,随着你代码量的增加,虽然代码引用次数的模块 moduleId 越小,越不容易变化,但是难免还是不确定的。

默认情况下,模块的 id 是这个模块在模块数组中的索引。OccurenceOrderPlugin 会将引用次数多的模块放在前面,在每次编译时模块的顺序都是一致的,如果你修改代码时新增或删除了一些模块,这将可能会影响到所有模块的 id。

最佳实践方案是通过 HashedModuleIdsPlugin 这个插件,这个插件会根据模块的相对路径生成一个长度只有四位的字符串作为模块的 id,既隐藏了模块的路径信息,又减少了模块 id 的长度。

这样一来,改变 moduleId 的方式就只有文件路径的改变了,只要你的文件路径值不变,生成四位的字符串就不变,hash 值也不变。增加或删除业务代码模块不会对 moduleid 产生任何影响。

module.exports = {

 plugins: [

 new webpack.HashedModuleIdsPlugin(),

 // 放在最前面

 // ...

 ]

}

四、chunkID

实际情况中分块的个数的顺序在多次编译之间大多都是固定的, 不太容易发生变化。

这里涉及的只是比较基础的模块拆分,还有一些其它情况没有考虑到,比如异步加载组件中包含公共模块,可以再次将公共模块进行抽离。形成异步公共 chunk 模块。有想深入学习的可以看这篇文章:Webpack 大法之 Code Splitting

webpack 做缓存的一些注意点

  1. CSS 文件 hash 值失效的问题
  2. 不建议线上发布使用 DllPlugin 插件

CSS 文件 hash 值失效的问题:

ExtractTextPlugin 有个比较严重的问题,那就是它生成文件名所用的[chunkhash]是直接取自于引用该 css 代码段的 js chunk ;换句话说,如果我只是修改 css 代码段,而不动 js 代码,那么最后生成出来的 css 文件名依然没有变化。

所以我们需要将 ExtractTextPlugin 中的 chunkhash 改为 contenthash,顾名思义,contenthash 代表的是文本文件内容的 hash 值,也就是只有 style 文件的 hash 值。这样编译出来的 js 和 css 文件就有独立的 hash 值了。

module.exports = {

 plugins: [

 // ...

 new ExtractTextPlugin({

  filename: `css/[name].[contenthash:8].css`,

 }),

 ]

}

如果你使用的是 webpack2,webpack3,那么恭喜你,这样就足够了,js 文件和 css 文件修改都不会影响到相互的 hash 值。那如果你使用的是 webpack1,那么就会出现问题。

具体来讲就是 webpack1 和 webpack 在计算 chunkhash 值得不同:

webpack1 在涉及的时候并没有考虑像 ExtractTextPlugin 会将模块内容抽离的问题,所以它在计算 chunkhash 的时候是通过打包之前模块内容去计算的,也就是说在计算的时候 css 内容也包含在内,之后才将 css 内容抽离成单独的文件,

那么就会出现:如果只修改了 css 文件,未修改引用的 js 文件,那么编译输出的 js 文件的 hash 值也会改变。

对此,webpack2 做了改进,它是基于打包后文件内容来计算 hash 值的,所以是在 ExtractTextPlugin 抽离 css 代码之后,所以就不存在上述这样的问题。如果不幸的你还在使用 webpack1,那么推荐你使用 md5-hash-webpack-plugin 插件来改变 webpack 计算 hash 的策略。

不建议线上发布使用 DllPlugin 插件

为什么这么说呢?因为最近有朋友来问我,他们 leader 不让在线上用 DllPlugin 插件,来问我为什么?

DllPlugin 本身有几个缺点:

  1. 首先你需要额外多配置一份 webpack 配置,增加工作量。
  2. 其中一个页面用到了一个体积很大的第三方依赖库而其它页面根本不需要用到,但若直接将它打包在 dll.js 里很不值得,每次页面打开都要去加载这段无用的代码,无法使用到 webpack2 的 Code Splitting 功能。
  3. 第一次打开的时候需要下载 dll 文件,因为你把很多库全部打在一起了,导致 dll 文件很大,首次进入页面加载速度很慢。

虽然你可以打包成 dll 文件,然后让浏览器去读取缓存,这样下次就不用再去请求,比如你用 lodash 其中一个函数,而你用dll会将整个 lodash 文件打进去,这就会导致你加载无用代码过多,不利于首屏渲染时间。

我认为的正确的姿势是:

  1. 像 React、Vue 这样整体性偏强的库,可以生成 vendor 第三方库来去做缓存,因为你一般技术体系是固定的,一个站点里面基本上都会用到统一技术体系,所以生成 vendor 库用于缓存。
  2. 像 antd、lodash 这种功能性组件库,可以通过 tree shaking 来进行消除,只保留有用的代码,千万不要直接打到 vendor 第三方库里,不然你将大量执行无用的代码。

结语

好了,感觉我又扯了很多,最近在看 webpack 确实收获不少,希望大家能从文章中也能有所收获。另外推荐再次推荐一下我之前写的文章,能够更好地帮你理解文件缓存机制:深入理解 webpack 文件打包机制

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • webpack学习笔记之优化缓存、合并、懒加载
  • webpack进阶——缓存与独立打包的用法
  • webpack独立打包和缓存处理详解
(0)

相关推荐

  • webpack学习笔记之优化缓存、合并、懒加载

    除了的webpack基本配置,还可以进一步添加配置,优化合并文件,加快编译速度.下面是生产环境配置文件webpack.production.js,与wenbpack.config.js相比其不需要一些dev-tools,dev-server和jshint校验等,将与开发相关的东西删掉.下面的介绍均以此代码配置作参考. /*生成环境配置文件:不需要一些dev-tools,dev-server和jshint校验等.和开发有关的东西删掉*/ var webpack = require('webpack

  • webpack进阶——缓存与独立打包的用法

    本文介绍了webpack进阶--缓存与独立打包的用法,分享给大家,希望对大家有帮助 先来看看最基础的webpack配置: var path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } } 在index.js中引入了lodash库: src/index.js: import

  • webpack独立打包和缓存处理详解

    前言 先前写了一篇webpack入门的文章<webpack入门必知必会>,简单介绍了webpack拆分.打包.压缩的使用方法.本文将在上篇文章的基础上进一步讲解在使用webpack构建的项目中存在的优化方案与解决方法. 上篇文章中写了一份webpack最基本的配置文件来打包压缩我们的代码: var path = require('path'); module.exports = { entry: './app/index.js', output: { filename: 'bundle.js'

  • 浅谈Webpack 持久化缓存实践

    前言 最近在看 webpack 如何做持久化缓存的内容,发现其中还是有一些坑点的,正好有时间就将它们整理总结一下,读完本文你大致能够明白: 什么是持久化缓存,为什么做持久化缓存? webpack 如何做持久化缓存? webpack 做缓存的一些注意点. 持久化缓存 首先我们需要去解释一下,什么是持久化缓存,在现在前后端分离的应用大行其道的背景下,前端 html,css,js 往往是以一种静态资源文件的形式存在于服务器,通过接口来获取数据来展示动态内容.这就涉及到公司如何去部署前端代码的问题,所以

  • 浅谈Webpack自动化构建实践指南

    由于现在的博客是使用wordpress搭建,自己得经常修改过一些代码,但是修改第三方源码真的比较痛苦,于是决定计划开始使用React + Node.js / Python开发新博客项目,最终替换当前博客代码,方便以后博客的维护和更新,也能实现自我开发技术,架构设计,解决问题能力的提升,同时记录下整个开发历程,总结,分享,希望能与读者们一起进步.本篇介绍如何使用Webpack和Babel,Eslint,documentation.js等搭建项目开发环境和生产环境,也算项目的准备工作,下一期计划介绍

  • 浅谈webpack打包之后的文件过大的解决方法

    以前一直使用 create-react-app 这个脚手架进行 react 开发,后面因为一些自定义的配置,转而使用 webpack 搭建一套自己的脚手架.但是在使用 webpack 打包之后发现,纳尼?怎么文件这么大??? 于是研究了一下如何处理 webpack 打包之后文件太大的情况,简单记录下来. 首先配置全局变量 首先,通过指定环境,告诉 webpack 我们当前处于 production 环境中,要按照 production 的方式去打包. //指定环境,将process.env.NO

  • 浅谈Webpack是如何打包CommonJS的

    目录 一.书写代码 二.使用webpack打包编译 三.解析 CommonJS 是 Node 中的一种模块化规范,其是一种运行在 Node 环境下的代码,这种代码是不能直接运行到浏览器环境中的.但是在日常使用 webpack 的项目中不用做额外的处理,我们也能使用 CommonJS 来书写代码,那么 webpack 在这背后做了什么呢? 我们这里不看编译时,只看运行时 一.书写代码 使用yarn init -y命令初始化一个package.json文件. 接着yarn add webpack安装

  • 浅谈webpack下的AOP式无侵入注入

    说起来, 面向切面编程(AOP)自从诞生之日起,一直都是计算机科学领域十分热门的话题,但是很奇怪的是,在前端圈子里,探讨AOP的文章似乎并不是多,而且多数拘泥在给出理论,然后实现个片段的定式)难免陷入了形而上学的尴尬境地,本文列举了两个生产环境的实际例子论述webpack和AOP预编译处理的结合,意在抛砖引玉.当然,笔者能力有限,如果有觉得不妥之处,还请大家积极的反馈出来, 共同进步哈. 重要的概念 AOP: 面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. Joi

  • 浅谈webpack打包生成的bundle.js文件过大的问题

    问题 使用webpack进行打包时,发现bundle.js竟然有2M多. 解决办法 网上有去除插件.提取第三方库.压缩代码等方法. 还有一个比较容易忽略的原因就是开了sourcemap 在生产环境中,应使用devtool: false 关闭sourcemap后bundle.js的大小从2.46M降到302k 以上这篇浅谈webpack打包生成的bundle.js文件过大的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们. 您可能感兴趣的文章: 彻底解决 webpa

  • 浅谈webpack打包过程中因为图片的路径导致的问题

    最近在制作一个自己的个人博客的时候遇到这么一个问题, 在CSS中使用了相对路径来充当背景图片, 如下所示: 然后将整个工程使用webpack打包之后, 在浏览器上运行却报错了, 报错如下: 也就是说, 打包之后这个图片文件找不到了, 那么原因出在哪里呢? 先来看一下我在webpack.config.js文件中的配置: 在这里其实我的loader并没有使用错误的, 图片对应的就是使用url-loader来处理. 那么再来看一下通过webpack打包之后的目录: 发现dist文件夹中出现了我们想要打

  • 浅谈webpack构建工具配置和常用插件总结

    webpack构建工具已经火了好几年,也是当下狠火狠方便的构建工具,我们没有理由不去学习.既然选择webpack就要跟着时代走,我们要追随大牛的步伐,大牛等等我. 一.webpack基础 在根目录使用npm init 命令创建package.json,创建过程中一路回车. 本地安装webpack命令:npm install webpack webpack-cli --save-dev 安装成功后写入package.js的devDependencies中,可以通过 npm node_modules

  • 浅谈Django 页面缓存的cache_key是如何生成的

    页面缓存 e.g. @cache_page(time_out, key_prefix=key_prefix) def my_view(): ... 默认情况下,将使用配置中的default cache cache_page 装饰器是由缓存中间件 CacheMiddleware 转换而来的 CacheMiddleware 继承了 UpdateCacheMiddleware 和 FetchFromCacheMiddleware UpdateCacheMiddleware 继承自 Middleware

  • 浅谈Mybatis二级缓存的缺陷

    一级缓存默认是开启的(但是整合了Spring,Mybatis的一级缓存默认就失效了) 二级缓存是要手动配置开启的(二级缓存是mapper级别的缓存,可以跨SqlSession) Mybatis二级缓存 开启二级缓存(xml方式):在*Mapper.xml中加入如下代码 <!--eviction: 清空缓存的策略 readOnly: 是否只读 flushInterval: 每个60秒刷新一次缓存 size: 内存大小,最多存储结果对象或者列表的512个引用 --> <cache readO

随机推荐