webpack打包问题,app.css,vendor.css,vendor manifestt.css包含相同的css的问题

在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
查到可以用CSS Module,但是要重写已写好的css文件,工作量太大,有没有别的方法?
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
用scss,有嵌套功能,每个页面一个id,每个页面的css都放在对应的id内。
也要重写css,也不难,就是加id和花括号。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
在style加 scoped
例如:&style lang="less" scoped&
同步到新浪微博
分享到微博?
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。webpack打包之后,文件过大的问题怎么解决?
优化devtool中的source-map.
dev-tool提供了很多种选项,用来增强我们debug的能力,我们熟知的有:source-map,inline-source-map,cheap-source-map等等。
Devtool官方文档,
Devtool配置对比,
webpack sourcemap 选项多种模式的一些解释,
如果你的文件在打包之后突然变成好几M,那么不用想,肯定是因为source-map的原因。source-map在开发阶段确实很好用,调试起来很方便,但是在生产环境下就没必要部署了。
建议在prod环境下关闭source-map。
剥离css文件,单独打包
安装webpack插件extract-text-webpack-plugin。
npm install extract-text-webpack-plugin --save-dev。
使用方法:
new ExtractTextPlugin('static/css/styles.[contenthash].css'),
这里使用了contenthash,webpack会根据内容去生成hash值。
使用UglifyJSPlugin压缩。
通过UglifyJSPlugin可以压缩我们的*.js文件。
安装方法: npm install uglifyjs-webpack-plugin --save-dev。
UglifyJSPlugin详细用法
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
plugins: [
new UglifyJSPlugin({
parallel: 4,
uglifyOptions: {
comments: false,
beautify: false,
compress: {
warnings: false
cache: true,
提取公共依赖
使用CommonsChunkPlugin插件,将多个js文件进行提取,建立一个独立的文件。这个文件包含一些共用模块,浏这样览器只在刚开始的时候加载一次,便缓存起来供后续使用。而不用每次访问一个新界面时,再去加载一个更大的文件。
app:'./entry',
vendor:['react','other-lib'],
new Webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
开启gzip压缩
我们使用compression-webpack-plugin插件进行压缩。
安装:npm install compression-webpack-plugin --save-dev。
compression-webpack-plugin 详细用法
const CompressionPlugin = require(&compression-webpack-plugin&);
new CompressionPlugin({
asset: '[path].gz[query]', //目标资源名称。[file] 会被替换成原资源。[path] 会被替换成原资源路径,[query] 替换成原查询字符串
algorithm: 'gzip',//算法
test: new RegExp(
'\\.(js|css)$'
//压缩 js 与 css
threshold: 10240,//只处理比这个值大的资源。按字节计算
minRatio: 0.8//只有压缩率比这个值小的资源才会被处理
压缩结果:
开启html压缩,自动添加上面生成的静态资源
添加插件html-webpack-plugin
安装: npm install html-webpack-plugin --save-dev
new HtmlWebpackPlugin({
title: '',
template: __dirname + '/../public/index.html',
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minify: true,
minifyURLs: true,
chunksSortMode:'dependency'webpack 2 打包实战(下)
配置favicon
在src目录中放一张favicon.png, 然后src/index.html的&head& 中插入:
&link rel="icon"type="image/png"href="favicon.png"&
修改webpack 配置:
test:/.html$/,
loader:'html-loader',
html-loader接受attrs参数, 表示什么标签的什么属性需要调用webpack的loader进行打包.
比如&img&标签的src属性, webpack会把&img&引用的图片打包, 然后src的属性值替换为打包后的路径.
使用什么loader代码, 同样是在module.rules定义中使用匹配的规则.
如果html-loader不指定attrs参数, 默认值是img:src, 意味着会默认打包&img&标签的图片.
这里我们加上&link&标签的href属性, 用来打包入口index.html引入的favicon.png文件.
attrs:['img:src','link:href']
匹配favicon.png
上面的html-loader会把入口index.html引用的favicon.png图标文件解析出来进行打包
打包规则就按照这里指定的loader执行
test:/favicon.png$/,
// 使用file-loader
loader:'file-loader',
// name: 指定文件输出名
// [name]是源文件名, 不包含后缀. [ext]为后缀. [hash]为源文件的hash值,
// 这里我们保持文件名, 在后面跟上hash, 防止浏览器读取过期的缓存文件.
name:'[name].[ext]?[hash]'
// 图片文件的加载配置增加一个exclude参数
test:/.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(?.+)?$/,
// 排除favicon.png, 因为它已经由上面的loader处理了. 如果不排除掉, 它会被这个loader再处理一遍
exclude:/favicon.png$/,
loader:'url-loader',
limit:10000
其实html-webpack-plugin接受一个favicon参数, 可以指定favicon文件路径, 会自动打包插入到html文件中. 但它有个bug, 打包后的文件名路径不带hash, 就算有hash, 它也是[hash], 而不是[chunkhash], 导致修改代码也会改变favicon打包输出的文件名. issue中提到的favicons-webpack-plugin倒是可以用, 但它依赖PhantomJS, 非常大.
开发环境允许其他电脑访问
webpack配置devServer.host为`0.0.0.0`即可.
打包时自定义部分参数
在多人开发时, 每个人可能需要有自己的配置, 比如说webpack-dev-server监听的端口号, 如果写死在webpack配置里, 而那个端口号在某个同学的电脑上被其他进程占用了, 简单粗暴的修改webpack.config.js会导致提交代码后其他同学的端口也被改掉.
还有一点就是开发环境/测试环境/生产环境的部分webpack配置是不同的, 比如publicPath在生产环境可能要配置一个CDN地址.
我们在根目录建立一个文件夹config, 里面创建3个配置文件:
default.js: 生产环境
module.exports ={
publicPath:'http://cdn.example.com/assets/'
dev.js: 默认开发环境
module.exports ={
publicPath:'/assets/',
devServer:{
port:8100,
'/api/auth/':{
target:'http://api.example.dev',
changeOrigin:true,
pathRewrite:{'^/api':''}
'/api/pay/':{
target:'http://pay.example.dev',
changeOrigin:true,
pathRewrite:{'^/api':''}
local.js: 个人本地环境, 在dev.js基础上修改部分参数 .
constconfig =require('./dev')
config.devServer.port =8200
module.exports =config
package.json修改s:
"local":"npm run dev --config=local",
"dev":"webpack-dev-server -d --hot --env.dev --env.config dev",
"build":"rimraf dist && webpack -p"
webpack配置修改:
consturl =require('url')
module.exports =(options ={})=&{
constconfig =require('./config/'+(process.env.npm_config_config ||options.config ||'default'))
devServer:config.devServer ?{
host:'0.0.0.0',
port:config.devServer.port,
proxy:config.devServer.proxy,
historyApiFallback:{
index:url.parse(config.publicPath).pathname
}:undefined,
这里的关键是npm run传进来的自定义参数可以通过process.env.npm_config_*获得. 参数中如果有-会被转成_
--env.*传进来的参数可以通过options.*获得. 我们优先使用npm run指定的配置文件. 这样我们可以在命令行覆盖s中指定的配置文件:
npm run dev --config=CONFIG_NAME
local命令就是这样做的.
这样, 当我们执行npm run dev时使用的是dev.js, 执行npm run local使用local.js, 执行npm run build使用default.js.
config.devServer.proxy用来配置后端api的反向代理, ajax /api/auth/*的请求会被转发到http://api.example.dev/auth/*, /api/pay/*的请求会被转发到http://api.example.dev/pay/*.
changeOrigin会修改HTTP请求头中的Host为target的域名, 这里会被改为api.example.dev
pathRewrite用来改写URL, 这里我们把/api前缀去掉.
还有一点, 我们不需要把自己个人用的配置文件提交到git, 所以我们在.gitignore 中加入:
!config/default.js
!config/dev.js
把config目录排除掉, 但是保留生产环境和dev默认配置文件.
webpack-dev-server处理带后缀名的文件的特殊规则
当处理带后缀名的请求时, 比如 http://localhost:8100/bar.do , webpack-dev-server会认为它应该是一个实际存在的文件, 就算找不到该文件, 也不会fallback到index.html, 而是返回404. 但在SPA应用中这不是我们希望的. 幸好webpack-dev-server有一个配置选项disableDotRule: true可以禁用这个规则, 使带后缀的文件当不存在时也能fallback到index.html
historyApiFallback:{
index:url.parse(config.publicPath).pathname,
disableDotRule:true
代码中插入环境变量
在业务代码中, 有些变量在开发环境和生产环境是不同的, 比如域名, 后台API地址等. 还有开发环境可能需要打印调试信息等.
我们可以使用DefinePlugin插件在打包时往代码中插入需要的环境变量 ,
constpkgInfo =require('./package.json')
module.exports =(options ={})=&{
constconfig =require('./config/'+(process.env.npm_config_config ||options.config ||'default')).default
newwebpack.DefinePlugin({
DEBUG:Boolean(options.dev),
VERSION:JSON.stringify(pkgInfo.version),
CONFIG:JSON.stringify(config.runtimeConfig)
DefinePlugin插件的原理很简单, 如果我们在代码中写:
console.log('Debug');
它会做类似这样的处理:
'console.log(DEBUG)'.replace('DEBUG',true)
console.log(true)
这里有一点需要注意, 像这里的VERSION, 如果我们不对pkgInfo.version做JSON.stringify(),
console.log(VERSION)
然后做替换操作:
'console.log(VERSION)'.replace('VERSION','1.0.0')
console.log(1.0.0)
这样语法就错误了. 所以, 我们需要JSON.stringify(pkgInfo.version)转一下变成'"1.0.0"', 替换的时候才会带引号.
还有一点, webpack打包压缩的时候, 会把代码进行优化, 比如:
if(DEBUG){
console.log('debug mode')
console.log('production mode')
会被编译成:
if(false){
console.log('debug mode')
console.log('production mode')
然后压缩优化为:
console.log('production mode')
简化import路径
文件a引入文件b时, b的路径是相对于a文件所在目录的. 如果a和b在不同的目录, 藏得又深, 写起来就会很麻烦:
importb from'../../../components/b'
为了方便, 我们可以定义一个路径别名(alias):
'~':resolve(__dirname,'src')
这样, 我们可以以`src`目录为基础路径来`import`文件:
importb from'~/components/b'
html中的&img&标签没法使用这个别名功能, 但html-loader有一个root参数, 可以使 / 开头的文件相对于root目录解析.
test:/.html$/,
loader:'html-loader',
root:resolve(__dirname,'src'),
attrs:['img:src','link:href']
那么, &img src="/favicon.png"&就能顺利指向到src目录下的favicon.png文件, 不需要关心当前文件和目标文件的相对路径.
PS: 在调试&img&标签的时候遇到一个坑, html-loader会解析&!-- --&注释中的内容, 之前在注释中写的
大于10kb的图片,图片会被储存到输出目录,src会被替换为打包后的路径
&img src="/assets/f78661bef717cf2cc2c2e.png"&
之前因为没有加root参数, 所以`/`开头的文件名不会被解析, 加了root导致编译时报错, 找不到该文件. 大家记住这一点.
优化babel编译后的代码性能
babel编译后的代码一般会造成性能损失, babel提供了一个loose选项, 使编译后的代码不需要完全遵循ES6规定, 简化编译后的代码, 提高代码执行效率:
package.json:
"presets":[
"loose":true
但这么做会有兼容性的风险, 可能会导致ES6源码理应的执行结果和编译后的ES5代码的实际结果并不一致. 如果代码没有遇到实际的效率瓶颈, 官方不建议使用loose模式.
使用webpack 2自带的ES6模块处理功能
我们目前的配置, babel会把ES6模块定义转为CommonJS定义, 但webpack自己可以处理import和export, 而且webpack处理import时会做代码优化, 把没用到的部分代码删除掉. 因此我们通过babel提供的modules: false选项把ES6模块转为CommonJS模块的功能给关闭掉.
package.json:
"presets":[
"loose":true,
"modules":false
使用autoprefixer自动创建css的vendor prefixes
css有一个很麻烦的问题就是比较新的css属性在各个浏览器里是要加前缀的, 我们可以使用autoprefixer工具自动创建这些浏览器规则, 那么我们的css中只需要写:
:fullscreen a {
display:flex
autoprefixer会编译成:
:-webkit-full-screen a {
display:-webkit-box;
display:flex
:-moz-full-screen a {
display:flex
:-ms-fullscreen a {
display:-ms-flexbox;
display:flex
:fullscreen a {
display:-webkit-box;
display:-ms-flexbox;
display:flex
首先, 我们用npm 安装它:
npm install postcss-loader autoprefixer --save-dev
autoprefixer是postcss的一个插件, 所以我们也要安装postcss的webpack loader.
修改一下webpack的css rule:
test:/.css$/,
use:['style-loader','css-loader','postcss-loader']
然后创建文件postcss.config.js:
module.exports ={
require('autoprefixer')()
编译前清空dist目录
不清空的话上次编译生成的文件会遗留在dist目录中, 我们最好先把目录清空一下. macOS/Linux下可以用rm -rf dist搞定, 考虑到跨平台的需求, 我们可以用rimraf:
npm install rimraf --save-dev
package.json修改一下:
"build":"rimraf dist && webpack -p --env.config production"
传统的多页面网站(MPA)能否用webpack打包?
对于多页面网站, 我们最多的是用Grunt或Gulp来打包, 因为这种简单的页面对模块化编程的需求不高. 但如果你喜欢上使用import来引入库, 那么我们仍然可以使用webpack来打包.
MPA意味着并没不是一个单一的html入口和js入口, 而是每个页面对应一个html和多个js. 那么我们可以把项目结构设计为:
├── dist
├── package.json
├── node_modules
├── src
│ ├── components
│ ├── libs
|├── favicon.png
|├── vendor.js 所有页面公用的第三方库
│ └── pages 页面放这里
|├── foo 编译后生成 http://localhost:8100/foo.html
||├── index.html
||├── index.js
||├── style.css
||└── pic.png
|└── bar http://localhost:8100/bar.html
|├── index.html
|├── index.js
|├── style.css
|└── baz http://localhost:8100/bar/baz.html
|├── index.html
|├── index.js
|└── style.css
└── webpack.config.js
这里每个页面的index.html是个完整的从&!DOCTYPE html&开头到&/html&结束的页面, 这些文件都要用html-webpack-plugin处理. index.js是每个页面的业务逻辑, 全部作为入口js配置到entry中. 页面公用的第三方库仍然打包进vendor.js. 这里我们需要用glob库来把这些文件都筛选出来批量操作 .
npm install glob --save-dev
webpack.config.js修改的地方:
constglob =require('glob')
module.exports =(options ={})=&{
constentries =glob.sync('./src/**/index.js')
constentryJsList ={}
constentryHtmlList =[]
for(constpath ofentries){
constchunkName =path.slice('./src/pages/'.length,-'/index.js'.length)
entryJsList[chunkName]=path
entryHtmlList.push(newHtmlWebpackPlugin({
template:path.replace('index.js','index.html'),
filename:chunkName +'.html',
chunks:['manifest','vendor',chunkName]
entry:Object.assign({
vendor:'./src/vendor'
},entryJsList),
...entryHtmlList,
代码在examples/mpa目录.
为什么不使用webpack.config.babel.js
部分同学可能知道webpack可以读取webpack.config.babel.js, 它会先调用babel将文件编译后再执行. 但这里有两个坑:
1. 由于我们的package.json中的babel配置指定了modules: false, 所以babel并不会转码import, 这导致编译后的webpack配置文件仍然无法在node.js中执行, 解决方案是package.json不指定modules: false, 而在babel-loader中的options中配置babel. 这样webpack.config.babel.js会使用package.json的babel配置编译, 而webpack编译的js会使用babel-loader指定的配置编译.
test:/.js$/,
exclude:/node_modules/,
loader:'babel-loader',
loose:true,
modules:false
'eslint-loader'
2. postcss的配置不支持先用babel转码, 这导致了我们的配置文件格式的不统一.
综上, 还是只在src目录中的文件使用ES6模块规范会比较方便一点.
通过这篇文章, 我想大家应该学会了webpack的正确打开姿势. 虽然我没有提及如何用webpack来编译React和vue.js, 但大家可以想到, 无非是安装一些loader和plugin来处理jsx和vue格式的文件, 那时难度就不在于webpack了, 而是代码架构组织的问题了. 具体的大家自己去摸索一下. 以后有时间我会把脚手架整理一下放到github上, 供大家参考 .
作者:@华尔街见闻技术团队
原文:https://zhuanlan.zhihu.com/p/
责任编辑:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
今日搜狐热点07:46:27 UTC
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
app:path.resolve(__dirname, 'src/index.js'),
vendor: ['react', 'react-dom', 'react-router']
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[hash:8].js'
loaders: [
test: /\.js[x]?$/,
loaders: ['babel'],
exclude: [path.resolve(__dirname, 'node_modules')]
test: /\.css/,
ExtractTextPlugin.extract('style-loader','css-loader!postcss-loader')
test: /\.(gif|jpg|png)\??.*$/,
loader: 'url-loader?limit=8192&name=img/[hash:8].[name].[ext]'
test: /\.(woff|woff2|svg|eot|ttf)\??.*$/,
loader: 'url-loader?limit=8192&name=font/[hash:8].[name].[ext]'
plugins: [
new ExtractTextPlugin("./styles/app.[hash:8].css", {
allChunks: true
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor'],
new webpack.optimize.UglifyJsPlugin({
comments: false,
compress:{
warnings:false
new HtmlWebpackPlugin({
template: './src/index.html',
inject: true,
new webpack.optimize.DedupePlugin()
配置如上 现在的问题是
打包生成的目录 引用生成的静态资源路径不正确 样式的引用如今都是相对与样式文件当前文件夹下的想知道有没什么解决办法?
08:12:19 UTC
test: /\.css/,
ExtractTextPlugin.extract('style-loader','css-loader!postcss-loader',{
publicPath:'../'
修改这样就好了使用loaders每引入一种loader,就相当于告诉了webpack:当遇到某种类型的文件时,就使用该loader来解析。less项目中使用了less,解析less文件需要使用3个loader:style-loader,css-loader,以及less-loader。$ npm install style-loader css-loader less-loader less 其中less-loader用来将less解析为css,css-loader能够解析import以及url()的语法,而style-loader可以将生成的css插入到HTML文档中(注意最后只生成一个bundle.js)。&所以webpack.config.js中解析less的配置如下:module: { & &rules: [ & & & &{ & & & & & &test: '/\.less$/',
& & & & & &use: ['style-loader', 'css-loader', 'less-loader']
& & & &} & &]}jsx以及es6因为webpack默认是可以解析commonJS,AMD以及es6的模块语法的,所以即使不使用任何的laoder,import以及require语法也可以直接使用。但是除了import/export这些模块语法,webpack是不会动你其他代码的。所以,如果我们在项目中使用es6、JSX等语法,还需要添加babel-loader。$ npm install babel-core babel-loader babel-preset-es2015 babel-preset-react --save-devwebpack.config.js中解析jsx以及es6的配置如下:module: { & &rules: [ & & & &test: /\.(js|jsx)$/, & & & &exclude: /node_modules/,
& & & &loader: 'babel-loader', & & & &query: [ & & & & & & &presets: ['react', 'es2015'] & & & &] & &]}提取css通过上面的配置,我们可以愉快地在项目中使用es6以及jsx语法了,并且最终会打包出唯一的一个bundle.js文件,然后再html中引用bundle.js即可。&但是,还有一点不足。项目中所有的样式都被打包到了bundle.js文件中,所以浏览器必须等待bundle.js完全加载完毕之后,才可以给文档加上样式,这样一来,我们就没办法利用浏览器的异步加载css以及并行加载的优势了。extract-text-webpack-plugin所以,我们可以把所有的css打包到一个单独的样式文件中。这就需要一个webpack插件,叫做extract-text-webpack-plugin。$ npm install extract-text-webpack-plugin在修改我们的webpack.config.js如下:const ExtractTextPlugin = require('extract-text-webpack-plugin');module.exports = { & &entry: './index.js', & & & &output: { & & & &filename: 'bundle.js', & & & &path: path.join(__dirname, 'public'), & &}, & &module: { & & & &rules: [ & & & & & & & { & & & & & & & & & &test: /\.less$/, & & & & & & & & & &use: ExtractTextPlugin.extract({ & & & & & & & & & & & & fallback: 'style-loader', & & & & & & & & & & & & use: ['css-loader', 'less-loader'] & & & & & & & & & &}) & & & & & & & } & & & &] & &}, & &plugins: [ & & & &new ExtractTextPlugin('index.css') & & &]};现在在控制台运行webpack –config webpack.config.js,我们会发现生成了一个bundle.js,还有一个index.css,可以将它们直接引入html中去。一点副作用extract-text-webpack-plugin可能会让css无法实现热重载(后面会讲到),所以我们最好只在生产环境中使用这个插件。提取第三方js模块上面我们把css单独提取出来了,但是bundle.js依旧可能是一个很大的文件,因为我们将一些第三方库打包了进去,比如lodash和react等。&这样做依旧有一个缺点: 每次修改了项目代码,哪怕只是一点点,整个bundle.js必须重新打包,浏览器也必须重新从服务器拉取新的bundle.js,这样就无法利用浏览器缓存静态资源的优势。&假如我们将不经常变化的第三方js库和我们的项目代码分开打包为vendor.js和app.js,那么当我们修改了项目代码并重新打包,只有app.js发生了变化,这样浏览器就只需要重新从服务器拉取新的app.js就可以了。&接下来我们就来这样做。多个入口修改options.entry以及options.output如下:: { & &app: './index.js', & &// 项目代码入口 & &vendor: ['react', 'react-dom', 'redux', 'react-redux'] // 第三方js库}: { & &filename: '[name].[chunkHash]js', &// 对于多入口,这里必须使用占位符 & &path: path.join(__dirname, 'public'),}再次打包,在public目录下生成了两个js文件(当然还有index.css):vendor.[hash].js以及app.[hash].js。文件名中的[hash]代表的是根据文件的内容所生成的hash值。CommonsChunkPlugin但是我们发现,第三方的js库竟然同时打包进了这两个js文件中。这是因为webpack将从每个入口文件开始分析依赖,这将得到多颗独立的依赖树,并且分别打包,互不干扰。如果我们指向将第三方模块打包到vendor.js中,我们还需要使用CommonsChunkPlugin这个插件。const webpack = require('webpack');plugins: [ & &new webpack.optimize.CommonsChunkPlugin({ & & & &name: 'vendor' & & & &})]运行webpack,发现公共的js库只打包在了vendor.[hash].js中,而app.[hash].js减小了很多。manifest现在终于可以安心的改项目代码了,可是,当我们改了一点项目代码,重新使用webpack来打包时,发现public目录下竟然又多出了两个文件app.[hash].js以及vendor.[hash].js。这与我们预想的不同,vendor应该保持不变才对,为什么又重新打包了一份呢?这也意味着我们没办法享受到浏览器缓存带来的好处,因为vendor的hash变了,我们每build一次,浏览器必须要重新加载vendor。&翻阅wepack的官方文档,对这个问题解释如下:&每次build,webpack都生成一些webpack运行时代码来帮助webpack完成它的工作。当只打包出一个文件时,这些运行时代码自然就在这个文件中,如果我们像上面那样打包出两个文件,那么运行时代码将被打包到公共模块中,正是vendor.js中。&为了解决这个问题,我们需要把这些运行时代码单独提到一个manifest.js中。尽管我们又多打包了一个文件,但是我们的vendor.js再也不会发生变化了,这使得我们可以享受浏览器缓存带来的性能提升。&修改配置文件如下:plugins: [ & &new webpack.optimize.CommonsChunkPlugin({ & & & &names: ['vendor', 'manifest'] & & & &})]按需加载通过以上配置,我们已经可以使用webpack来完成最基本的打包工作了。但是,浏览器加载的资源总比它实际用得到的多,如果我们能在应用中实现按需加载(懒加载),那么,我们应用的性能可能再进一步提升。&我们有两种方式可以实现按需加载,下面将一一介绍,不过在此之前,可以先看一下这篇文章。import()import()方法是es6中关于模块加载的一个规范,用于在运行时动态加载模块。该函数将一个模块名当作参数,并且返回一个Promise对象,这意味着当模块加载没有成功时,我们可以作出一些处理。&webpack把import()当作一个代码分离点,并且把通过import引入的模块,单独打包到一个chunk。&下面看import()在项目代码中的使用:handleSubmit () { & &import('moment') & & & & & &.then(moment =& { & & & & & &console.log(moment().format('MMMM Do YYYY, h:mm:ss a')); & & & &}) & & & &.catch(err =& { & & & & & &console.log('模块加载失败!') & & & &});}现在我们来配置webpack,让其支持这种动态加载模块语法。首先,需要安装bebel-plugin-syntax-dynamic-import插件:// 注意,我们之前已经安装过bable-core babel-loader等插件$ npm install babel-plugin-syntax-dynamic-import // 同时安装moment.js$ npm install moment 然后配置webpack如下:{ & &test: /\.(js|jsx)$/, & &exclude: /node_modules/, & &use: [{ & & & &loader: 'babel-loader', & & & &query: { & & & & & &presets: ['react', 'es2015'], & & & & & &plugins: ['syntax-dynamic-import'] & & & & & &} & &}]}运行wepack,发现除了manifest.js,vendor.js,app.js,index.css以外,还生成了0.js,打开0.js可以发现,其中包含了moment模块的所有代码,这样,在应用初次加载的时候,是不需要加载0.js的,只有当用户点击了提交按钮之后,0.js才会从浏览器拉取并执行。&此外我们还可以给这些按需加载的代码块规定名称;: { & &filename: [name].js, & &path: path.join(__dirname, 'public'), & &chunkFileName: '[id].chunk.js' & &}require.ensure()除了es6的提案中的import(),webpack还为我们提供了require.ensure()的方式,并且这种方式不需要额外安装插件,还可以指定打包的chunk名称。&其api如下:require.ensure(dependencies: String[], callback: function(require), chunkeName: string);我们要明白的一点是:require.ensure()只会加载dependencies中指定的依赖模块,但不执行。如果想执行,可以在callback中调用require。&现在来看一个案例,假设在testModules目录下有3js文件,名称和内容如下:console.log('aaaaaaaaaaaaaaaaaaaa');console.log('bbbbbbbbbbbbbbbbbbbb');console.log('ccccccccccccccccccc');下面我们再另外一个js模块中的handleSubmit()中按需加载这3个模块:require.ensure([], require =& { & &require('../testModules/a'); & &require('../testModules/b');}, 'test'); require.ensure(['../testModules/a', '../testModules/b'], require =& { & &require('../testModules/b'); & &require('../testModules/c');})以上还需要在实践中多多体验。更愉快的开发到目前位置,我们可以使用es6,less,jsx等语法以及按需加载的功能了,但是对于开发环境来讲,这种体验还不是很好。因为我们每次修改完代码,都必须手动执行webpack –config webpack.config.js,等待webpack打包出新文件之后,我们再去浏览器刷新页面,才能看到效果。&我们想要的效果应该是这个样子:每次修改完less,按下ctrl+s,浏览器不会整页刷新,而是直接把新样式运用到页面中每次修改完js,保存后,浏览器自动整页刷新有source-map,方便排除bug如果是纯前端项目,使用webpack-dev-server就可以实现热重载了,不过现在我们要把它整合到一个后台使用express的项目中。下面我们来一一实现,先介绍热重载依靠的两个插件webpack-dev-middleware和webpack-hot-middleware。webpack-dev-middleware这是一个只应该在开发环境中使用的webpack插件,它可以监听项目代码的变化,自动打包文件到内存中,并且server这些文件。&当自动编译还未完成的时候,如果浏览器已经发来了请求,那么这个请求将会被阻塞,直到文件编译完成。这看起来就像网速很慢,卡住了一样。$ npm install webpack-dev-middleware --save-dev配置webpack.dev.config.js如下;: { & &filename: [name].js, & &path: path.join(__dirname, 'public'), & &chunkFilename: '[id].[name].chunk.js', & &publicPath: '/assets/' & &}上面配置中的publicPath字段比较难理解,它指明了浏览器按需加载或者加载外部资源(图片,文件)时候的路径。在这里我们配置它,是为指明了webpack-dev-middleware serve 内存文件的路径(继续往下看)。&现在我们可以在html中这样引用资源了,并且按需加载文件的url也会指向/assets/1.test.chunk.js这样的。 src=&/assert/manifest.js&&& src=&/assert/vendor.js&&& src=&/assert/app.js&&&最后,我们来正式使用webpack-dev-middleware:const path = requrie('path');const express = rquire('express');const webpack = require('webpack');const webpackDevMiddleware = rquire('webpack-dev-middleware');const webpackCofig = require('./webpack.dev.config.js');const app = express();const compiler = webpack(webpackConfig);app.use(webpackDevMiddleware({ & &publicPath: webpackConfig.output.publicPath & &}));app.get('/', function (req, res) { & &res.sendFile(path.join(__dirname, 'index.html'));});app.listen(3000, function () { & &console.log('listening on port 3000');});现在我们可以在3000端口访问应用了,并且每修改完代码,按下ctrl+s或者直接刷新浏览器,webpack都会自动打包文件到内存中。&这样,就不用每次都运行webpack –config webpaack.dev.config.js了,打包速度也快了很多。webpack-hot-middleware美中不足的是我们还需要手动刷新浏览器,现在我们通过wepack-hot-middleware来实现HMR。&webpack-hot-middleware只关心将一个客户端和一个服务器连接起来,并且订阅服务端资源的更新,最终将更新的资源应用与客户端(通过webpack的HMR api)。$ npm install --save-dev webpack-hot-middleware更改webpack的配置文件:const HotMiddlewareScript = 'webpack-hot-middleware/client?reload=true';entry: { & &app: ['index.js', HotMiddlewareScript], & &vendor: ['react', 'react-dom', 'redux', 'react-redux', HotMiddlewareScript]}plugins: [ & &new webpack.HotModuleReplacementPlugin()]配置app.js:const webpackHotMiddleware = rquire('webpack-hot-middleware');app.use(webpackHotMIddleware(compiler));现在重启app.js,并在浏览器中访问3000端口,发现控制台出现了[HMR] connected字样。并且我们修改js,发现webpack会自动打包,并通知浏览器刷新。&但是,当我们修改了less文件之后,却发现webpack只是自动打包了,新样式并没有被应用到浏览器中。&这是为什么呢?其实前面在讲extract-text-webpack-plugin的时候,我们已经提到过了:extract-text-webpack-plugin并不适合在开发环境中只用,它会导致css的热重载失效。&所以,我们把webpack.dev.config.js中关于extarct-text-webpack-plugin的部分去掉之后,发现一切跟我们预想的一样!react-hot-loader除此之外,针对react,还有一个可以实现热重载的工具,即react-hot-loader,这里就不详细介绍了,到官网看去吧!source-mapsource-map可以快速帮助我们定位bug,对于提升开发效率有很好的提升。&通过webpack来实现source-map非常简单,只需要添加一行配置:devtool: 'cheap-eval-source-map'devtool的值有8种,它们的特性以及适用环境都有很多不同。详细信息就看这里。最后webpack的基本应用到这里就先告一段落,还有很多高级的特性没有涉及,以后还需要在实践中慢慢摸索。&最后,放上工作成果图:&
转载请注明: &

我要回帖

更多关于 webpack manifest.js 的文章

 

随机推荐