【金沙棋牌官方平台】webpack前端自动化构建方案
带来的变化
其实这次升级带来了不少改变,但大部分其实对于普通用户来说是不需要关注的,比如这次升级带来的功能SideEffects
、Module Type’s Introduced
、WebAssembly Support
,基本平时是用不到的。我们主要关注那些对我们影响比较大的改动如:optimization.splitChunks
代替原有的CommonsChunkPlugin
(下篇文章会着重介绍),和Better Defaults-mode
更好的默认配置,这是大家稍微需要关注一下的。
如果想进一步了解
Tree Shaking
和SideEffects
的可见文末拓展阅读。
上图参考 Webpack 4 进阶
五、运行项目
在webStorm中打开项目,首先赶紧右击Project进行如下操作(否则会卡死,还有各种其他方法参见:
(1)启动安装:cnpm install
(2)然后npm run dev:跑起来~
(3)生成打包文件 :npm run build
然后你会发现项目多了个dist文件夹(用于部署到生产环境用,是打包压缩之后的src文件夹)
© 著作权归作者所有返回搜狐,查看更多
责任编辑:
十三、特别强调--webpack最优秀的地方:
预处理;按需加载;
webpack提供的require()方法和require.ensure()方法来实现,即AMD和commonJS规范;
先使用webpack自带的压缩,执行命令webpack -p

升级篇
原标题:vue-cli+webpack搭建vue最全分析
5、UMD:
是模块跨平台解决方案;
▍第四步:开启nginx的gzip功能
webpack打包编译后成一个文件,文件会比按需加载大。web项目在第一次加载的时候,文件太大的话会比较慢。所以要想办法把它的体积变小再变小。
contenthash
但使用 MiniCssExtractPlugin
有一个需求特别注意的地方,在默认文档中它是这样配置的:
new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: devMode ? "[name].css" : "[name].[hash].css", chunkFilename: devMode ? "[id].css" : "[id].[hash].css" });
1
2
3
4
5
6
|
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: devMode ? "[name].css" : "[name].[hash].css",
chunkFilename: devMode ? "[id].css" : "[id].[hash].css"
});
|
简单说明一下:
filename
是指在你入口文件entry
中引入生成出来的文件名,而chunkname
是指那些未被在入口文件entry
引入,但又通过按需加载(异步)模块的时候引入的文件。
在 copy
如上代码使用之后发现情况不对!每次改动一个xx.js
文件,它对应的 css
虽然没做任何改动,但它的 文件 hash 还是会发生变化。仔细对比发现原来是
hash
惹的祸。 6.f3bfa3af.css
=> 6.40bc56f6.css
但我这是根据官方文档来写的!为什么还有问题!后来在文档的最最最下面发下了这么一段话!
For long term caching use filename:
[contenthash].css
. Optionally add [name].
非常的不理解,这么关键的一句话会放在 Maintainers
还后面的地方,默认写在配置里面提示大家不是更好?有热心群众已经开了一个pr
,将文档默认配置为
contenthash
。chunkhash
=> contenthash
相关
issue。
这个真的蛮过分的,稍不注意就会让自己的 css
文件缓存无效。而且很多用户平时修改代码的时候都不会在意自己最终打包出来的
dist
文件夹中到底有哪些变化。所以这个问题可能就一直存在了。浪费了多少资源!人艰不拆!大家觉得
webpack 难用不是没道理的。
一、vue-cli介绍
vue-cli是一个基于nodeJs、用于快速搭建vue项目的 脚手架。
二、vue-cli安装、更新
安装过nodeJs 、cnpm 后,全局安装vue-cli(以后其他项目可直接使用):
cnpm install -g vue-cli
更新:
cnpm update vue-cli
查看安装成功否(有版本号就是成功,V大写)
vue -V
查看npm注册表里vue-cli版本号:
cnpm view vue-cli
三、vue-cli 使用
安装过webpack 、vue-cli后,可以开始搭建vue项目:
vue init webpack <Project Name>
eg:右击Git Base Here(如果你没有用git ,你也可以按住shift键右击选择“在此处打开命令窗口”,或者 cmd :cd project/lfxProject),如图:
or
ps:ESLint(一个java代码检测工具)、unit tests(单元测试)、Nightwatch(一个e2e用户界面测试工具)。
四、项目完成
项目结构如下:
各文件作用解析,如下:
1、build文件夹:
build文件夹的结构:
(1)build.js
'use strict'
require('./check-versions')() //调用版本检查
process.env.NODE_ENV = 'production' //将环境配置为生产环境
const ora = require('ora') //npm包 loading插件
const rm = require('rimraf') //npm包 用于删除文件
const path = require('path')//npm包 文件路径工具
const chalk = require('chalk')//npm包 在终端输出带颜色的文字
const webpack = require('webpack')//引入webpack.js
const config = require('../config')//引入配置文件
const webpackConfig = require('./webpack.prod.conf')//引入生产环境配置文件
// 在终端显示loading效果,并输出提示
const spinner = ora('building for production...')
spinner.start()
//先递归删除dist文件再生成新文件,避免冗余
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + 'nn')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.n' +
' Opening index.html over file:// won't work.n'
))
})
})
ps:require/export是一种nodeJs(commonJs规范)的依赖注入的方法,import/export是ES6语法,用于引入模块,在nodeJs中使用的ES6语法最终会使用babel工具(babel-loader)转化为ES5
(2)check-version.js:检测node和npm的版本,实现版本依赖
'use strict'
const chalk = require('chalk')
const semver = require('semver')//检查版本
const packageConfig = require('../package.json')
const shell = require('shelljs')//shelljs 模块重新包装了 child_process,调用系统命令更加方便
function exec (cmd) {//返回通过child_process模块的新建子进程,执行 Unix 系统命令后转成没有空格的字符串
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),//使用semver格式化版本
versionRequirement: packageConfig.engines.node //获取package.json中设置的node版本
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),// 自动调用npm --version命令,并且把参数返回给exec函数,从而获取纯净的版本号
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
//若版本号不符合package.json文件中指定的版本号,就报错
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}
(3)utils.js:utils是工具的意思,是一个用来处理css的文件,这个文件包含了三个工具函数:
- 生成静态资源的路径
- 生成 ExtractTextPlugin对象或loader字符串
- 生成 style-loader的配置
var path = require('path')// node自带的文件路径工具
var config = require('../config')// 配置文件
var ExtractTextPlugin = require('extract-text-webpack-plugin')// 提取css的插件
/** @method assertsPath 生成静态资源的路径(判断开发环境和生产环境,为config文件中index.js文件中定义assetsSubDirectory)
* @param {String} _path 相对于静态资源文件夹的文件路径
* @return {String} 静态资源完整路径
*/
exports.assetsPath = function (_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
//nodeJs path提供用于处理文件路径的工具;path.posix提供对路径方法的POSIX(可移植性操作系统接口)特定实现的访问(可跨平台); path.posix.join与path.join一样,不过总是以 posix 兼容的方式交互
return path.posix.join(assetsSubDirectory, _path)
}
/**@method cssLoaders 生成处理css的loaders配置,使用css-loader和postcssLoader,通过options.usePostCSS属性来判断是否使用postcssLoader中压缩等方法
* @param {Object} option = {sourceMap: true,// 是否开启 sourceMapextract: true // 是否提取css}生成配置
* @return {Object} 处理css的loaders配置对象
*/
exports.cssLoaders = function (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
/**@method generateLoaders 生成 ExtractTextPlugin对象或loader字符串
* @param {Array} loaders loader名称数组
* @return {String|Object} ExtractTextPlugin对象或loader字符串
*/
function generateLoaders (loader, loaderOptions) {
var loaders = [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// ExtractTextPlugin提取css(当上面的loaders未能正确引入时,使用vue-style-loader)
if (options.extract) {// 生产环境中,默认为true
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {//返回vue-style-loader连接loaders的最终值
return ['vue-style-loader'].concat(loaders)
}
}
return {
css: generateLoaders(),//需要css-loader 和 vue-style-loader
postcss: generateLoaders(),//需要css-loader、postcssLoader 和 vue-style-loader
less: generateLoaders('less'),//需要less-loader 和 vue-style-loader
sass: generateLoaders('sass', { indentedSyntax: true }),//需要sass-loader 和 vue-style-loader
scss: generateLoaders('sass'),//需要sass-loader 和 vue-style-loader
stylus: generateLoaders('stylus'),//需要stylus-loader 和 vue-style-loader
styl: generateLoaders('stylus')//需要stylus-loader 和 vue-style-loader
}
}
/**@method styleLoaders 生成 style-loader的配置
* @param {Object} options 生成配置
* @return {Array} style-loader的配置
*/
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
//将各种css,less,sass等综合在一起得出结果输出output
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\.' + extension + '$'),
use: loader
})
}
return output
}
(4)vue-loader.conf.js:处理.vue文件,解析这个文件中的每个语言块(template、、style),转换成js可用的js模块。
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
//生产环境,提取css样式到单独文件
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
//编译时将“引入路径”转换为require调用,使其可由webpack处理
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
(5)webpack.base.conf.js:开发、测试、生产环境的公共基础配置文件,配置输出环境,配置模块resolve和插件等
'use strict'
const path = require('path')// node自带的文件路径工具
const utils = require('./utils')// 工具函数集合
const config = require('../config')// 配置文件
const vueLoaderConfig = require('./vue-loader.conf')// 工具函数集合
/**
* 获取"绝对路径"
* @method resolve
* @param {String} dir 相对于本文件的路径
* @return {String} 绝对路径
*/
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
context: path.resolve(__dirname, '../'),
//入口js文件(默认为单页面所以只有app一个入口)
entry: {
app: './src/main.js'
},
//配置出口
output: {
path: config.build.assetsRoot,//打包编译的根路径(dist)
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath//发布路径
},
resolve: {
extensions: ['.js', '.vue', '.json'],// 自动补全的扩展名
//别名配置
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),// eg:"src/components" => "@/components"
}
},
module: {
rules: [
//使用vue-loader将vue文件编译转换为js
{
test: /.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
//通过babel-loader将ES6编译压缩成ES5
{
test: /.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
//使用url-loader处理(图片、音像、字体),超过10000编译成
{
test: /.(png|jpe?g|gif|svg)(?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /.(woff2?|eot|ttf|otf)(?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
//nodeJs全局变量/模块,防止webpack注入一些nodeJs的东西到vue中
node: {
setImmediate: false,
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
(6)webpack.dev.conf.js:webpack配置开发环境中的入口
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')//webpack-merge实现合并
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')//webpack的提示错误和日志信息的插件
const portfinder = require('portfinder')// 查看空闲端口位置,默认情况下搜索8000这个端口
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
devtool: config.dev.devtool,//调试模式
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {//使用 HTML5 History API 时, 404 响应替代为 index.html
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,//热重载
contentBase: false, // 提供静态文件访问
compress: true,//压缩
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,//npm run dev 时自动打开浏览器
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,// 显示warning 和 error 信息
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,//api代理
quiet: true, //控制台打印警告和错误(用FriendlyErrorsPlugin 为 true)
watchOptions: {// 检测文件改动
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),//模块热替换插件,修改模块时不需要刷新页面
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitsPlugin(),//webpack编译错误的时候,中断打包进程,防止错误代码打包到文件中
// 将打包编译好的代码插入index.html
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// 提取static assets 中css 复制到dist/static文件
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']//忽略.*的文件
}
])
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => { //查找端口号
if (err) {
reject(err)
} else {
//端口被占用时就重新设置evn和devServer的端口
process.env.PORT = port
devWebpackConfig.devServer.port = port
// npm run dev成功的友情提示
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: ],
},
s: config.dev.notifys
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})
(7)webpack.dev.prod.js:webpack配置生产环境中的入口
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,//是否开启调试模式
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({//压缩js
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
new ExtractTextPlugin({//提取静态文件,减少请求
filename: utils.assetsPath('css/[name].[contenthash].css'),
allChunks: true,
}),
new OptimizeCSSPlugin({//提取优化压缩后(删除来自不同组件的冗余代码)的css
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
new HtmlWebpackPlugin({ //html打包压缩到index.html
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,//删除注释
collapseWhitespace: true,//删除空格
removeAttributeQuotes: true//删除属性的引号
},
chunksSortMode: 'dependency'//模块排序,按照我们需要的顺序排序
}),
new webpack.HashedModuleIdsPlugin(),
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.optimize.CommonsChunkPlugin({ // node_modules中的任何所需模块都提取到vendor
name: 'vendor',
minChunks (module) {
return (
module.resource &&
/.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
new CopyWebpackPlugin([//复制static中的静态资源(默认到dist里面)
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
2、config文件夹:
config文件夹的结构:
(1) dev.env.js和prod.env.js:分别配置:开发环境和生产环境。这个可以根据公司业务结合后端需求配置需要区分开发环境和测试环境的属性
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})
ps:webpack-merge用于实现合并类似于ES6的Object.assign()
'use strict'
module.exports = {
NODE_ENV: '"production"'
}
(*注意属性值要用“‘’”双层引住),访问(获取值)时直接用:
process.env.属性名
ps:process(进程)是nodejs的一个全局变量,process.env 属性返回一个用户环境信息的对象
(2)index.js配置解析:
'use strict';
const path = require('path');
module.exports = {
// ===================开发环境配置
dev: {
assetsSubDirectory: 'static',//静态资源文件夹(一般存放css、js、image等文件)
assetsPublicPath: '/',//根目录
proxyTable: {},//配置API代理,可利用该属性解决跨域的问题
host: 'localhost', // 可以被 process.env.HOST 覆盖
port: 3030, // 可以被 process.env.PORT 覆盖
autoOpenBrowser: true,//编译后自动打开浏览器页面 + host",默认"false"),设置路由重定向自动打开您的默认页面
errorOverlay: true,//浏览器错误提示
notifys: true,//跨平台错误提示
poll: false, //webpack提供的使用文件系统(file system)获取文件改动的通知devServer.watchOptions(监控文件改动)
devtool: 'cheap-module-eval-source-map',//webpack提供的用来调试的模式,有多个不同值代表不同的调试模式
cacheBusting: true,// 配合devtool的配置,当给文件名插入新的hash导致清除缓存时是否生成source-map
cssSourceMap: true //记录代码压缩前的位置信息,当产生错误时直接定位到未压缩前的位置,方便调试
},
// ========================生产环境配置
build: {
index: path.resolve(__dirname, '../dist/index.html'),//编译后"首页面"生成的绝对路径和名字
assetsRoot: path.resolve(__dirname, '../dist'),//打包编译的根路径(默认dist,存放打包压缩后的代码)
assetsSubDirectory: 'static',//静态资源文件夹(一般存放css、js、image等文件)
assetsPublicPath: '/',//发布的根目录(dist文件夹所在路径)
productionSourceMap: true,//是否开启source-map
devtool: '#source-map',//(详细参见:)
productionGzip: false,//是否压缩
productionGzipExtensions: ['js', 'css'],//unit的gzip命令用来压缩文件(gzip模式下需要压缩的文件的扩展名有js和css)
bundleAnalyzerReport: process.env.npm_config_report //是否开启打包后的分析报告
}
};
3、node_modules文件夹:存放npm install时根据package.json配置生成的npm安装包的文件夹
4、src文件夹:我们需要在src文件夹中开发代码,打包时webpack会根据build中的规则(build规则依赖于config中的配置)将src打包压缩到dist文件夹在浏览器中运行
(1)assets文件:用于存放静态资源(css、image),assets打包时路径会经过webpack中的file-loader编译(因此,assets需要使用绝对路径)成js
(2)components文件夹:用来存放 .vue 组件(实现复用等功能,如:过滤器,列表项等)
(3)router文件夹:在router/index.js文件中配置页面路由
(4)App.vue:是整个项目的主组件,所有页面都是通过使用<router-view/>开放入口在App.vue下进行切换的(所有的路由都是App.vue的子组件)
(5)main.js:入口js文件(全局js,你可以在这里:初始化vue实例、require/import需要的插件、注入router路由、引入store状态管理)
5、static文件夹:webpack默认存放静态文件(css、image)的文件夹,与assets不同的是:static在打包时会直接复制一个同名文件夹到dist文件夹里(不会经过编译,可使用相对路径)
6、其他文件:
(1).babelrc:浏览器解析的兼容配置,该文件主要是对预设(presets)和插件(plugins)进行配置,因此不同的转译器作用不同的配置项,大致可分为:语法转义器、补丁转义器、sx和flow插件
(2).editorconfig:用于配置代码格式(配合代码检查工具使用,如:ESLint,团队开发时可统一代码风格),这里配置的代码规范规则优先级高于编辑器默认的代码格式化规则 。
(3).gitignore:配置git提交时需要忽略的文件
(4)postcssrc.js: autoprefixer(自动补全css样式的浏览器前缀);postcss-import(@import引入语法)、CSS Modules(规定样式作用域)
(5)index.html:项目入口页面,编译之后所有代码将插入到这来
(6)package.json:npm的配置文件(npm install根据package.json下载对应版本的安装包)
(7)package.lock.json:npm install(安装)时锁定各包的版本号
(8)README.md:项目使用说明
2、怎么管理:
gulp:处理html压缩/预处理/条件编译,图片压缩,精灵图自动合并等任务;
webpack:管理模块化,构建js/css。
压缩与优化
打包 css 之后查看源码,我们发现它并没有帮我们做代码压缩,这时候需要使用 optimize-css-assets-webpack-plugin 这个插件,它不仅能帮你压缩 css 还能优化你的代码。
//配置 optimization: { minimizer: [new OptimizeCSSAssetsPlugin()]; }
1
2
3
4
|
//配置
optimization: {
minimizer: [new OptimizeCSSAssetsPlugin()];
}
|
如上图测试用例所示,由于optimize-css-assets-webpack-plugin
这个插件默认使用了
cssnano 来作 css 优化,
所以它不仅压缩了代码、删掉了代码中无用的注释、还去除了冗余的 css、优化了
css 的书写顺序,优化了你的代码 margin: 10px 20px 10px 20px;
=>margin:10px 20px;
。同时大大减小了你 css
的文件大小。更多优化的细节见文档。
3、AMD:
异步require,通过接口define() 异步加载;可并行加载多个模块;适合浏览器。
创建package.json
npm init
在项目文件夹下新建一个package.json并填写项目信息,项目名称以及版本号等项目信息
与 extract-text-webpack-plugin 区别
由于webpack4
对 css 模块支持的完善以及在处理 css
文件提取的方式上也做了些调整,所以之前我们一直使用的extract-text-webpack-plugin
也完成了它的历史使命,将让位于mini-css-extract-plugin
。
使用方式也很简单,大家看着 文档 抄就可以了。
它与extract-text-webpack-plugin
最大的区别是:它在code spliting
的时候会将原先内联写在每一个
js chunk bundle
的 css,单独拆成了一个个 css 文件。
原先 css 是这样内联在 js 文件里的:
将 css 独立拆包最大的好处就是 js 和 css 的改动,不会影响对方。比如我改了
js 文件并不会导致 css
文件的缓存失效。而且现在它自动会配合optimization.splitChunks
的配置,可以自定义拆分
css
文件,比如我单独配置了element-ui
作为单独一个bundle
,它会自动也将它的样式单独打包成一个
css 文件,不会像以前默认将第三方的 css 全部打包成一个几十甚至上百 KB
的app.xxx.css
文件了。
1、传统:
<script src=“module.js”>标签;
▍第一步:搭建环境
手摸手,带你用合理的姿势使用webpack4(上)
2018/08/26 · 基础技术 · webpack
原文出处: 华尔街见闻技术团队 - 花裤衩
前几天 webpack 作者 Tobias Koppers
发布了一篇新的文章 webpack 4.0 to 4.16: Did you
know?(需翻墙),总结了一下webpack 4
发布以来,做了哪些调整和优化。
并且说自己正在着手开发 webpack 5
。
Oh you are still on webpack 3. I’m sorry, what is blocking you? We already working on webpack 5, so your stack might be outdated soon…
翻译成中文就是:
正好我也在使用一个文档生成工具
docz(安利一波)
也最低需要webpack 4+
,新版webpack
性能提高了不少,而且webpack 4
都已经发布五个多月了,想必应该已经没什么坑了,应该可以安心的按照别人写的升级攻略升级了。之前一直迟迟不升级完全是被去年被
webpack 3
坑怕了。它在 code splitting
的情况下
CommonsChunkPlugin
会完全失效。过了好一段时间才修复,欲哭无泪。
所以这次我等了快大半年才准备升级到webpack 4
但万万没想到还是遇到了不少的问题!
有很多之前遗留的问题还是没有很好地解决。但最主要的问题还是它的文档有所欠缺,已经废除了的东西如commonsChunkPlugin
还在官方文档中到处出现,很多重要的东西却一笔带过,甚至没写,需要用户自己去看源码才能解决。
还比如在v4.16.0
版本中废除了 optimization.occurrenceOrder
、optimization.namedChunks
、optimization.hashedModuleIds
、optimization.namedModules
这几个配置项,替换成了optimization.moduleIds
和
optimization.chunkIds
,但文档完中全没有任何体现,所以你在新版本中还按照文档那样配置其实是没有任何效果的。
最新最完整的文档还是看他项目的配置WebpackOptions.json,强烈建议遇到不清楚的配置项可以看这个,因为它一定保证是和最新代码同步的。
吐槽了这么多,我们言归正传。由于本次手摸手篇幅有些长,所以拆解成了上下两篇文章:
- 上篇 —
就是普通的在
webpack 3
的基础上升级,要做哪些操作和遇到了哪些坑 - 下篇 — 是在
webpack 4
下怎么合理的打包和拆包,并且如何最大化利用long term caching
本文章不是手摸手从零教你 webpack 配置,所以并不会讲太多很基础的配置问题。比如如何处理 css 文件,如何配置 webpack-dev-server,讲述 file-loader 和 url-loader 之间的区别等等,有需求的推荐看 官方文档 或者 survivejs 出的一个系列教程。或者推荐看我司的另一篇 wbepack 入门文章,已同步到 webpack4 传送门。
其中关键的配置这儿简单介绍如下:
context:上下文。
entry:入口文件,是所有依赖关系的入口,webpack从这个入口开始静态解析,分析模块之间的依赖关系。
output:打包输出的配置。
devtools:SourceMap选项,便于开发模式下调试。
watch:监听模式,增量更新,开发必备!
profile:优化。
cache:webpack构建的过程中会生成很多临时的文件,打开cache可以让这些临时的文件缓存起来,从而更快的构建。
module.loaders:如前文介绍,loaders用来对文件做预处理。这样webpack就可以打包任何静态文件。
resolve.alias:模块别名,这样可以更方便的引用模块。
plugins:如前文介绍,webpack的一些内置功能均是以插件的形式提供。
安装node.js和npm
npm 是 javaScript的包管理工具,类似maven的依赖管理,
用它来下载我们需要的模块。
Node.js下载地址
默认配置
webpack 4 引入了零配置
的概念,被
parcel 刺激到了。
不管效果怎样,这改变还是值得称赞的。
最近又新出了 Fastpack 可以关注一下。
言归正题,我们来看看 webpack 默认帮我们做了些什么?
development
模式下,默认开启了NamedChunksPlugin
和NamedModulesPlugin
方便调试,提供了更完整的错误信息,更快的重新编译的速度。
module.exports = { + mode: 'development' - devtool: 'eval', - plugins: [ - new webpack.NamedModulesPlugin(), - new webpack.NamedChunksPlugin(), - new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }), - ] }
1
2
3
4
5
6
7
8
9
|
module.exports = {
+ mode: 'development'
- devtool: 'eval',
- plugins: [
- new webpack.NamedModulesPlugin(),
- new webpack.NamedChunksPlugin(),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }),
- ]
}
|
production
模式下,由于提供了splitChunks
和minimize
,所以基本零配置,代码就会自动分割、压缩、优化,同时
webpack 也会自动帮你 Scope hoisting
和 Tree-shaking
。
module.exports = { + mode: 'production', - plugins: [ - new UglifyJsPlugin(/* ... */), - new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }), - new webpack.optimize.ModuleConcatenationPlugin(), - new webpack.NoEmitOnErrorsPlugin() - ] }
1
2
3
4
5
6
7
8
9
|
module.exports = {
+ mode: 'production',
- plugins: [
- new UglifyJsPlugin(/* ... */),
- new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }),
- new webpack.optimize.ModuleConcatenationPlugin(),
- new webpack.NoEmitOnErrorsPlugin()
- ]
}
|
webpack
一直以来最饱受诟病的就是其配置门槛极高,配置内容极其复杂和繁琐,容易让人从入门到放弃,而它的后起之秀如
rollup、parcel
等均在配置流程上做了极大的优化,做到开箱即用,所以webpack 4
也从中借鉴了不少经验来提升自身的配置效率。愿世间再也不需要 webpack
配置工程师。
九、代码分块:chunk--程序块;
(1)CommonJs require.ensure(dependencies, callback);
(2)AMD require(dependencies, callback)
1. extensions
// 当后缀名为空的时候,会自动识别并不缺
resolve: {
extensions: ['', '.js']
}
mini-css-extract-plugin
1、目的:
前端工程化;更好的管理前端代码;
开启后:

这样子就把我们刚刚接近2M的项目代码压缩到150Kb。
这里再简单说明一下几种 hash 的区别:
- hash
hash
和每次 build
有关,没有任何改变的情况下,每次编译出来的
hash
都是一样的,但当你改变了任何一点东西,它的hash
就会发生改变。
简单理解,你改了任何东西,hash
就会和上次不一样了。
- chunkhash
chunkhash
是根据具体每一个模块文件自己的的内容包括它的依赖计算所得的hash
,所以某个文件的改动只会影响它本身的hash
,不会影响其它文件。
- contenthash
它的出现主要是为了解决,让css
文件不受js
文件的影响。比如foo.css
被foo.js
引用了,所以它们共用相同的chunkhash
值。但这样子是有问题的,如果foo.js
修改了代码,css
文件就算内容没有任何改变,由于是该模块的
hash
发生了改变,其css
文件的hash
也会随之改变。
这个时候我们就可以使用contenthash
了,保证即使css
文件所处的模块里有任何内容的改变,只要
css 文件内容不变,那么它的hash
就不会发生变化。
contenthash
你可以简单理解为是 moduleId
+ content
所生成的
hash
。
十、分块类型:
(1)入口块 entry chunk;(2)普通块 normal chunk;
(3)初始块 initial chunk;(4)多入口块;
Tree-Shaking
这其实并不是 webpack 4 才提出来的概念,最早是
rollup 提出来并实现的,后来在
webpack 2 中就实现了,本次在 webpack 4 只是增加了
JSON Tree Shaking
和sideEffects
能让你能更好的摇。
不过这里还是要提一下,默认 webpack
是支持Tree-Shaking
的,但在你的项目中可能会因为babel
的原因导致它失效。
因为Tree Shaking
这个功能是基于ES6 modules
的静态特性检测,来找出未使用的代码,所以如果你使用了 babel
插件的时候,如:babel-preset-env,它默认会将模块打包成commonjs
,这样就会让Tree Shaking
失效了。
其实在 webpack 2 之后它自己就支持模块化处理。所以只要让 babel
不transform modules
就可以了。配置如下:
// .babelrc { "presets": [ ["env", { modules: false, ... }] ] }
1
2
3
4
5
6
7
8
9
|
// .babelrc
{
"presets": [
["env", {
modules: false,
...
}]
]
}
|
顺便说一下都 8102
年了,请不要在使用babel-preset-esxxxx
系列了,请用babel-preset-env
,相关文章
再见,babel-preset-2015。
十四、按需加载的时间戳:
rake任务;
开启nginx的gzip功能
官方文档:Module
ngx_http_gzip_module
翻译:Nginx官方文档翻译--ngx_http_gzip_module模块
gzip on;
gzip_proxied any;
gzip_disable "msie6";
gzip_comp_level 3;
gzip_min_length 4k;
gzip_types text/css text/xml application/javascript;
升级 webpack
首先将 webpack 升级到 4
之后,直接运行webpack --xxx
是不行的,因为新版本将命令行相关的东西单独拆了出去封装成了webpack-cli
。会报如下错误:
The CLI moved into a separate package: webpack-cli.
Please installwebpack-cli
in addition to webpack itself to use the CLI.
所有你需要安装npm install webpack-cli -D -S
。你也可将它安装在全局。
同时新版 webpack
需要Node.js 的最低支持版本为 6.11.5
不要忘了升级。如果还需要维护老项目可以使用
nvm 来做一下 node 版本管理。
升级所有依赖
因为webpack4
改了 它的hook
api
,所以所有的loaders
、plugins
都需要升级才能够适配。
可以使用命令行
npm outdated
,列出所以可以更新的包。免得再一个个去npm
找相对于的可用版本了。
反正把devDependencies
的依赖都升级一下,总归不会有错。
6、ES6:
尽量静态化;编译时就能确定模块的依赖关系及输入输出变量;CommonJs和AMD都只能在运行时确定这些东西。
优点:静态分析,提前编译;面向未来的标准;
缺点:
1、浏览器原生兼容性差,所以一般都编译成ES5。
2、目前可以拿来即用的模块少,生态差。
压缩成项目名称_版本号.zip
使用node.js的插件zip-local
安装并加入项目中:
npm install zip-local --save-dev
项目目录下建立zip.js
文件
// 从package.json文件中获取到项目名称和版本号
var packageInfo = require('./package.json');
var zip = packageInfo.name + "_" + packageInfo.version;
// 引入插件
var zipper = require('zip-local');
// 压缩dist文件夹
zipper.sync.zip("dist/").compress().save(zip + ".zip");
执行命令行 node zip
生成zip压缩文件。
前言
我一直认为模仿和借鉴是学习一个新东西最高效的方法。所以我建议还是通过借鉴一些成熟的
webpack 配置比较好。比如你项目是基于 react 生态圈的话可以借鉴
create-react-app
,下载之后npm run eject
就可以看到它详细的 webpack 配置了。vue
的话由于新版vue cli
不支持 eject
了,而且改用
webpack-chain来配置,所以借鉴起来可能会不太方便,主要配置
地址。觉得麻烦的话你可以直接借鉴
vue-element-admin
的
配置。或者你想自己发挥,你可以借鉴
webpack 官方的各种
examples,来组合你的配置。
2、commonJS:
协同require()同步加载,再通过exports或module.exports来导出需要暴露的接口;不能加载多个模块;不适合浏览器加载。
环境配置(webpack.config.js
)
"scripts": {
"production": "set NODE_ENV=production && webpack -p",
"test" : "set NODE_ENV=test && webpack"
}
执行npm run production / npm run test
这是以前的做法。通过设置环境变量NODE_ENV来判断打包的环境。在webpack.config.js中通过process.env.NODE_ENV获取这个值。
发现在mac上是可以的,但在windows底下。scripts中使用&&
是有问题的。后来改了一下。直接把webpack -p
这个指令当做生产环境。其他视为调试。
下部分内容
- 手摸手,带你用合理的姿势使用 webpack4 (下)
十二、怎么组织代码:
gulp那一套、webpack原生方案;
未压缩前,执行命令webpack

打包速度
webpack 4
在项目中实际测了下,普遍能提高 20%~30%的打包速度。
本文不准备太深入的讲解这部分内容,详细的打包优化速度可以参考slack 团队的这篇文章,掘金还有译文.
这里有几个建议来帮你加速 webpack 的打包速度。
首先你需要知道你目前打包慢,是慢在哪里。
我们可以用 speed-measure-webpack-plugin 这个插件,它能监控 webpack 每一步操作的耗时。如下图:
可以看出其实大部分打包花费的时间是在Uglifyjs
压缩代码。和前面的提升热更新的切入点差不多,查看source map
的正确与否,exclude/include
的正确使用等等。
使用新版的UglifyJsPlugin
的时候记住可以加上cache: true
、parall: true
,可以提搞代码打包压缩速度。更多配置可以参考
文档 或者
vue-cli 的
配置。
编译的时候还有还有一个很慢的原因是那些第三方库。比如echarts
、element-ui
其实都非常的大,比如echarts
打包完也还有
775kb。所以你想大大提高编译速度,可以将这些第三方库 externals
出去,使用script
的方式引入,或者使用
dll
的方式打包。经测试一般如echarts
这样大的包可以节省十几秒到几十秒不等。
还有可以使用一些并行执行 webpack 的库:如parallel-webpack、happypack。
顺便说一下,升级一下node
可能有惊喜。前不久将CI
里面的 node
版本依赖从 6.9.2
=> 8.11.3
,打包速度直接提升了一分多钟。
总之我觉得打包时间控制在差不多的范围内就可以了,没必要过分的优化。可能你研究了半天,改了一堆参数发现其实也就提升了几秒,但维护成本上去了,得不偿失。还不如升级 node、升级 webpack、升级你的编译环境的硬件水平来的实在和简单。
比如我司CI
使用的是腾讯云普通的的 8 核 16g
的机器,这个项目也是一个很大的后台管理项目
200+页面,引用了很多第三方的库,但没有使用什么happypack
、dll
,只是用了最新版的webpack4
,node@8.11.3
。
编译速度稳定在两分多钟,完全不觉得有什么要优化的必要。
十一、gulp&webpack整合:
转载请注明原作者以及链接,谢谢!
html-webpack-plugin
用最新版本的的 html-webpack-plugin
你可能还会遇到如下的错误:
throw new Error('Cyclic dependency' + nodeRep)
产生这个 bug 的原因是循环引用依赖,如果你没有这个问题可以忽略。
目前解决方案可以使用 Alpha
版本,npm i --save-dev html-webpack-plugin@next
或者加入chunksSortMode: 'none'
就可以了。
但仔细查看文档发现设置成chunksSortMode: 'none'
这样是会有问题的。
Allows to control how chunks should be sorted before they are included to the HTML.
这属性会决定你 chunks 的加载顺序,如果设置为none
,你的 chunk
加载在页面中加载的顺序就不能够保证了,可能会出现样式被覆盖的情况。比如我在app.css
里面修改了一个第三方库element-ui
的样式,通过加载顺序的先后来覆盖它,但由于设置为了none
,打包出来的结果变成了这样:
<link href="/app.8945fbfc.css" rel="stylesheet"> <link href="/chunk-elementUI.2db88087.css" rel="stylesheet">
1
2
|
<link href="/app.8945fbfc.css" rel="stylesheet">
<link href="/chunk-elementUI.2db88087.css" rel="stylesheet">
|
app.css
被先加载了,之前写的样式覆盖就失效了,除非你使用important
或者其它
css 权重的方式覆盖它,但这明显是不太合理的。
vue-cli
正好也有这个相关
issue,尤雨溪也在不使用@next
版本的基础上
hack
了它,有兴趣的可以自己研究一下,本人在项目中直接使用了@next
版本,也没遇到其它什么问题(除了不兼容
webpack 的 prefetch/preload
相关
issue)。两种方案都可以,自行选择。
其它 html-webpack-plugin
的配置和之前使用没有什么区别。
十五、常用插件:
1、解析压缩美化chunk:UglifyJsPlugin new
webpack.optimize.UglifyJsPlugin([options]);
2、分离css文件;ExtractTextPlugin var ExtractTextPlugin =
require("extract-text-webpack-plugin");
3、删除重复依赖;DedupePlugin new webpack.optimize.DedupePlugin();
4、跳过编译出错并记录;NoErrorsPlugin new webpack.NoErrorsPlugin();
5、提取公共模块;CommonsChunkPlugin new
webpack.optimize.CommonsChunkPlugin(options);
autoprefixer-loader
可以补全css代码
热更新速度
其实相对 webpack
线上打包速度,我更关心的本地开发热更新速度,毕竟这才是和我们每一个程序员每天真正打交道的东西,打包一般都会扔给CI
自动执行,而且一般项目每天也不会打包很多次。
webpack 4
一直说自己更好的利用了cache
提高了编译速度,但实测发现是有一定的提升,但当你一个项目,路由懒加载的页面多了之后,50+之后,热更新慢的问题会很明显,之前的文章中也提到过这个问题,原以为新版本会解决这个问题,但并没有。
不过你首先要排斥你的热更新慢不是,如:
- 没有使用合理的 Devtool souce map 导致
- 没有正确使用
exclude/include
处理了不需要处理的如
node_modules
- 在开发环境不要压缩代码
UglifyJs
、提取 css、babel polyfill、计算文件 hash 等不需要的操作
旧方案
最早的方案是开发环境中不是用路由懒加载了,只在线上环境中使用。封装一个_import
函数,通过环境变区分是否需要懒加载。
开发环境:
module.exports = file => require("@/views/" + file + ".vue").default;
1
|
module.exports = file => require("@/views/" + file + ".vue").default;
|
生成环境:
module.exports = file => () => import("@/views/" + file + ".vue");
1
|
module.exports = file => () => import("@/views/" + file + ".vue");
|
但由于 webpack
import
实现机制问题,会产生一定的副作用。如上面的写法就会导致@/views/
下的
所有.vue
文件都会被打包。不管你是否被依赖引用了,会多打包一些可能永远都用不到 js
代码。 相关
issue
目前新的解决方案思路还是一样的,只在生成模式中使用路由懒加载,本地开发不使用懒加载。但换了一种没副作用的实现方式。
新方案
使用babel
的 plugins
babel-plugin-dynamic-import-node。它只做一件事就是:将所有的import()
转化为require()
,这样就可以用这个插件将所有异步组件都用同步的方式引入了,并结合
BABEL_ENV
这个bebel
环境变量,让它只作用于开发环境下。将开发环境中所有import()
转化为require()
,这种方案解决了之前重复打包的问题,同时对代码的侵入性也很小,你平时写路由的时候只需要按照官方文档路由懒加载的方式就可以了,其它的都交给babel
来处理,当你不想用这个方案的时候,也只需要将它从babel
的 plugins
中移除就可以了。
具体代码:
首先在package.json
中增加BABEL_ENV
"dev": "BABEL_ENV=development webpack-dev-server XXXX"
1
|
"dev": "BABEL_ENV=development webpack-dev-server XXXX"
|
接着在.babelrc
只能加入babel-plugin-dynamic-import-node
这个plugins
,并让它只有在development
模式中才生效。
{ "env": { "development": { "plugins": ["dynamic-import-node"] } } }
1
2
3
4
5
6
7
|
{
"env": {
"development": {
"plugins": ["dynamic-import-node"]
}
}
}
|
之后就大功告成了,路由只要像平时一样写就可以了。文档
{ path: '/login', component: () => import('@/views/login/index')}
1
|
{ path: '/login', component: () => import('@/views/login/index')}
|
这样能大大提升你热更新的速度。基本两百加页面也能在2000ms
的热跟新完成,基本做到无感刷新。当然你的项目本身就不大页面也不多,完全没必要搞这些。当你的页面变化跟不是你写代码速度的时候再考虑也不迟。
三、webpack概念:
webpack是模块化管理工具,使用webpack可以对模块进行压缩、预处理、按需打包、按需加载等。
webpack resolve
这个是在webpack.config.js中去配置的一个对象。它的几个用法:
拓展阅读
- Webpack 4 和单页应用入门
- Webpack 中的 sideEffects 到底该怎么用?
- 你的 Tree-Shaking 并没什么卵用
- 对 webpack 文档的吐槽
- Tree-Shaking 性能优化实践 – 原理篇
再见,babel-preset-2015
1 赞 收藏 评论
六、webpack样式:
内嵌css(不推荐)、独立的css、公共的css样式;
webpack-dev-server
本地webserver
启动:webpack-dev-server
地址:http://localhost:port/webpack-dev-server/dist/index.html
文档地址
:webpack-dev-server文档
端口号默认是8080,可以在devServer中更改。每次改代码保存后,会自动编译并且刷新页面。还可以做到热替换
即不刷新页面可以更新。除此之外还有一个proxy
功能可以用来配置代理。
7、期望的模块:
可以兼容多种模块风格,尽量可以利用已有的代码,不仅仅只是 JavaScript 模块化,还有 CSS、图片、字体等资源也需要模块化。
webpack常用指令:
$ webpack // 最基本的启动webpack方法
$ webpack -w // 提供watch方法,实时进行打包更新
$ webpack -p // 对打包后的文件进行压缩
$ webpack -d // 提供source map,方便调试。
五、webpack配置及参数解析:
1、两份配置文件webpack.config.production.js/webpack.config.development.js,然后不同场景下,使用不同的配置文件。
2、通过module.exports返回函数,该函数能接受参数。
相对来说,第一种更简单,但是重复配置多;第二种更灵活,推荐第二种方式。
代码:webpack+anuglar.js
八、webpack对模块做了什么?
(1)非模块化代码;(2)Runtime & 模块;(3)AMD模块;(4)CommonJs;
▍第二步:编写webpack.config.js文件
七、模块化机制特点:
1、可以兼容多模块风格,无痛迁移老项目。
2、一切皆模块,js/css/图片/字体都是模块。
3、静态解析(确定依赖关系,输入输出的变量),按需打包,动态加载。(webpack最优秀的地方);
webpack提供的loaders可以对文件做预处理,从而实现了一切皆模块。
引入依赖
项目中要用到angular,npm install --save-dev angular
把依赖下载到文件夹node_modules
中。
通过require("angular")可以直接引入
。
4、CMD:
类似AMD;
2. alias
// 通过别名可以减少打包时间,项目中直接require("consts")重定向到指定的文件。
resolve: {
alias: {
consts: path.resolve(__dirname, "app/util/constant.js")
}
}
3、怎么整合:
(1)webpack-stream方案[不推荐];
(2)webpack原生方案;
webpack sourcemap
调试的时候可能需要知道这个页面对应了哪些文件。这里就要用到sourcemap。可以使用命令webpack -d
去生成,或者在webpack.config.js
中配置devtool
。
其实调试很多时候主要还是看样式来自哪个文件。所以这里简单一点,如果是测试环境。css-loader文件后边加上sourcemap。这样子编译的文件小,速度也快一些。我有时候配置devtool不起效果。也不知道为什么。
{ test: /.css$/, loader: "style!css?sourceMap!autoprefixer" }
一、模块化系统演进:
▍webpack模块化
webpack支持CommonJs和AMD规范。前者是同步加载,后者是异步加载。webpack加载的对象是module。它把静态资源都当做是模块。不仅仅只是js。还有css、html、图片等等都可以通过require
去引入,如引入图片:
var img = document.createElement("img");
img.src = require("../image/xxx.jpg");
document.body.appendChild(img);
模块输出:module.exports = ...
webpack 如何最佳配置?
webpack官方提供的配置方法是通过module.exports返回一个json,但是这种场景不灵活,不能适配多种场景。比如要解决:production模式和development模式,webpack的配置是有差异的,大致有两种思路。
1、两份配置文件webpack.config.production.js/webpack.config.development.js
,然后不同场景下,使用不同的配置文件。
2、通过module.exports返回函数,该函数能接受参数。
相对来说,第一种更简单,但是重复配置多;第二种更灵活,推荐第二种方式。那么,按返回函数的方式的配置代码架子如下:
module.exports = function(env) {
return { context: config.context,
entry: config.src, output: { path: path.join(config.jsDest, project),
filename: '[name].js',
chunkFilename: '[name].[chunkhash:8].js', publicPath: '/assets/' +
project + '/'
},
devtool: "eval", watch: false, profile: true, cache: true, module: {
loaders: getLoaders(env)
},
resolve: {
alias: getAlias(env)
},
plugins: getPlugins(env)
};
}
图片base64编码
图片这里使用url-loader,会过滤图片。
limit=8192
: 将图片大小小于8K的转为base64编码。
name=images/[name].[ext]
: 大于8k的图片则存放到dist/images底下。
二、gulp&webpack区别:
gulp是基于流的构建工具:all in
one的打包模式,输出一个js文件和一个css文件,优点是减少http请求,万金油方案。
webpack是模块化管理工具,使用webpack可以对模块进行压缩、预处理、打包、按需加载等。
entry
编译的入口Js文件。ouput
文件输出定义
编译后的文件名bundle.[hash].js
,这里用到hash
。每一次进行webpack打包,都会生成一个hash值,用它为文件命名,能让每次生成的文件名都不一样。浏览器访问也不会存在缓存问题。path.resolve
用来拼接路径,__dirname
是指当前路径的绝对路径。
四、webpack9个特征:
(1)插件化:webpack本身非常灵活,提供了丰富的插件接口。基于这些接口,webpack开发了很多插件作为内置功能。
(2)速度快:webpack使用异步IO以及多级缓存机制。所以webpack的速度是很快的,尤其是增量更新。
(3)丰富的Loaders:loaders用来对文件做预处理。这样webpack就可以打包任何静态文件。
(4)高适配性:webpack同时支持AMD/CommonJs/ES6模块方案。webpack会静态解析你的代码,自动帮你管理他们的依赖关系。此外,webpack对第三方库的兼容性很好。
(5)代码拆分:webpack可以将你的代码分片,从而实现按需打包。这种机制可以保证页面只加载需要的JS代码,减少首次请求的时间。
(6)优化:webpack提供了很多优化机制来减少打包输出的文件大小,不仅如此,它还提供了hash机制,来解决浏览器缓存问题。
(7)开发模式友好:webpack为开发模式也提供了很多辅助功能。比如SourceMap、热更新等。
(8)使用场景多:webpack不仅适用于web应用场景,也适用于Webworkers、Node.js场景。
(9)优点:webpack并不强制你使用某种模块化方案,而是通过兼容所有模块化方案让你无痛接入项目,当然这也是webpack牛逼的地方。
有了webpack,你可以随意选择你喜欢的模块化方案,至于怎么处理模块之间的依赖关系及如何按需打包,放轻松,webpack会帮你处理好的。
▍第三步:编译打包项目工程
ng-annotate-loader
是用来处理angular.js的代码。这是一小段代码:
angular.module('myApp', [])
.controller('testCtrl', function ($scope) {});
$scope
普通的压缩会把它当成参数压缩了。解决的方法,一种是写成数组
['$scope', function($scope) {}];
// 或者是
testCtrl.$inject = ['$scope'];
另外一种是使用ng-annotate
。编译之后,会自动补全
angular.module('myApp', [])
.controller('testCtrl', ["$scope", function ($scope) {}]);
我们项目的框架使用的是angular.js(v1.5.8)+ webpack,使用webpack主要是要想做到以下几点:
1.抛弃原来项目中的requireJs,gulp/grunt。模块化和打包均由webpack实现。
2.将项目资源大部分输出到一个bundle.js文件,减少浏览器首次加载时的资源请求数量。
3.使用npm安装框架依赖。不再引入项目中。避免项目代码提交时更改库代码。
4.进行代码压缩,并开启nginx的gzip,对代码再次进行压缩。
5.实现测试和生产环境使用不同的配置文件。
6.生成带有项目名称和版本号的文件夹并压缩(用于webApp)。
7.生成带有hash的bundle.js文件。可以解决浏览器因为缓存不刷新的问题(用于web项目)。因为项目情况,没有用到es6,scss等。没有进行代码转换。如果需要转码,只要找到对应的loader。npm安装后加载进来就可以了,这里不会提到。另外,webpack我也没用多久。如果哪里写的不好或者写错了,请指点,谢谢!
先奉上webpack文档地址:webpack
全局引入
有时候像jquery想要给它配置全局,使用$
去引入。
首先安装依赖到项目中
npm install --save-dev jquery
配置plugins,然后就可以在项目中使用。
plugins:[
new webpack.ProvidePlugin({
$:"jquery"
})
]
使用loader加载器对资源进行转换
module.exports = {
entry: path.resolve(__dirname, "app", "index.js"),
output: {
path: "dist",
filename: "bundle.[hash].js"
},
module: {
loaders: [
{
test: /.js$/,
exclude: /node_modules/,
loader:"ng-annotate"
}, {
test: /.css$/,
loader: "style!css!autoprefixer"
}, {
test: /.html$/,
exclude: /node_modules/,
loader:"html"
}, {
test: /.(ttf|eot|otf)$/,
loader: "file"
}, {
test: /.woff(2)?$/,
loader: "url?limit=8192&minetype=application/font-woff"
}, {
test: /.(png|jpg|gif|svg)$/,
loader: "url?limit=8192&name=images/[name].[ext]"
}
]
}
}
获取到指令的第二个参数,为-p
则判断为生产环境。
var isProductEnv = process.argv[2] === '-p';
然后可能需要不同环境配置一些变量。比如说HTTP请求的地址。因为这些都属于项目的内容。就不想在webpack.config.js中配置。直接配置到app文件夹中。在项目中判断当前环境,require
对应的JSON数据。
// webpack.config.js
var DefinePlugin = require('webpack').DefinePlugin;
var definePlugins = new DefinePlugin({
webpack_prod : isProductEnv
});
module.exports = {
...
plugins: [
definePlugins,
...
]
}
// index.js
if (webpack_prod) {
require('./env/product');
} else {
require('./env/test');
}
loader加载器的使用,如css-loader。
首先安装css-loader:npm install --save-dev css-loader
在配置文件中,通过正则绑定对应的loader,
可以省略loader
,写成css
/css-loader
均可。使用!
可以定义多个loader,?
后边配置参数
生成index.html
每一次生成的bundle.js文件因为带有hash,所以都是不同的。不能在index.html里边直接引入。这里要用到一个插件html-webpack-plugin
根据模板生成index.html页面(github地址)。
安装:npm install html-webpack-plugin --save-dev
引入:var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
filename: path.resolve([输出路径], 'index.html'),
template: path.resolve([输入路径], "index_template.html")
})
]
}
在模板index_template.html不需要引入bundle.js。生成index.html会自动引入。
npm安装插件
全局安装webpack:
npm install webpack -g
安装webpack-dev-server:
npm install webpack-dev-server -g
安装依赖到项目中并加入package.json
npm install [package] --save-dev/ npm install [package] --save
可能有时候会npm安装包比较慢甚至是安装不了,可以使用淘宝NPM镜像[cnpm]
删除依赖
npm uninstall [package]
目录结构:
webpack
|____app
| |____index.html
| |____index.js
|____package.json
|____webpack.config.js
|____zip.js
本文由金沙棋牌发布于金沙棋牌官方平台,转载请注明出处:【金沙棋牌官方平台】webpack前端自动化构建方案
关键词:
下一篇:没有了