安装依赖的简写说明:以vue为例
cnpm i vue -S // 生产依赖 等价于 --save
cnpm i vue-loader css-loader vue-template-compiler -D // 开发依赖 等价于--save-dev
文档很长,我在前面把坑都踩了,希望大家看原文的时候结合我的文档来学习,要有耐心看哦。毕竟祖国的未来还是要靠你们去建设的^_^。
Step1.先学习概念,大概30分钟
Step2.学习指南,这个内容比较多,照着上面的示例慢慢练习
1.指南(太简单了,略)
2.安装(略)
3.起步(略)
4.管理资源(略)
5.管理输出, HtmlWebpackPlugin 这里可能会遇到问题,那是因为没有在项目中安装webpack
clean-webpack-plugin 默认只能清除dist下的文件
引用图片:import src from "./g1.jpg" 不能直接src = "./g1.jpg";否则图片加载不了。
6.开发
6.1 配置 devtool:"inline-source-map" ,会跳到错误的源文件中,助力开发调试
6.2 "watch” :"webpack --watch" //不用安装,改代码--刷新页面--成功
6.3 1.webpack-dev-server 的使用,要安装,配置
2.webpack.json配置:"dev":"webpack-dev-server --open" // cnpm run dev 就启动了
3. webpack.config.js配置: devServer:{ contentBase:"./dist" }
// 以上配置告知 webpack-dev-server
,在 localhost:8080
下建立服务,将 dist
目录下的文件,作为可访问文件。
//选择了webpack-dev-server 就不要webpack-dev-middleware 和watch 了。
webpack-dev-server --hot // 也能起到模块热更新的作用
7.模块热替换,如下配置就可以自动更新了
以下几种loader也是热模块更新:
rules: [
+ {
+ test: /\.css$/,
+ use: ['style-loader', 'css-loader']
+ }
+ ]
还有:Vue Loader 、angular HMR也会热替换
8.tree shaking,配置webpack.json文件,去除被打包文件中没有用到的代码。
// 8.1通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)
8.2 生产环境会自动压缩打包的代码,
到这步的时候,webpack.json是这样的
{
"name": "webpack0827",
"sideEffects": false,
"version": "1.0.0",
"private": true,
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"watch": "webpack --watch",
"dev": "webpack-dev-server --open"
},
"author": "tangcc",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.10"
},
"devDependencies": {
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^1.0.0",
"file-loader": "^2.0.0",
"html-webpack-plugin": "^3.2.0",
"style-loader": "^0.23.0",
"webpack": "^4.17.1",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.6"
}
}
webpack.config.js 是这样的
const webpack = require('webpack');
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
// entry:"./src/index.js",
entry:{
app:"./src/index.js",
// print:"./src/print-edit1.js" // 做更新先干掉
},
// output:{
// filename :"bundle.js",
// path:path.resolve(__dirname,"dist")
// },
output:{
path:path.resolve(__dirname,"dist")
,filename:'[name].bundle.js'
}
,
module:{
rules:[
{
test: /.css$/,
use: [
'style-loader',
"css-loader"
]
},
{
test:/\.(png|svg|jpg|gif)$/,
use:["file-loader"]
},
// {
// include: path.resolve("node_modules", "lodash"),
// sideEffects: false
// }
]
}
,
plugins :[
new HtmlWebpackPlugin({
title: 'Output Management tnagcc'
}),
new CleanWebpackPlugin(["dist"])
,new webpack.NamedModulesPlugin() // 模块热替换
,new webpack.HotModuleReplacementPlugin() // 模块热替换
],
devtool : "inline-source-map",
devServer : { //告知 webpack-dev-server,在 localhost:8080 下建立服务,将 dist 目录下的文件,作为可访问文件。
contentBase:"./dist"
,hot:true // 模块热替换开启,要配合plugins ,还得手动改一下index文件
},
mode: "production"// 生产环境会自动压缩代码,注意,--optimize-minimize 标记也会在 webpack 内部调用 UglifyJsPlugin。
}
9.生产环境构建,要拆分配置文件了,所以目录结构要改
npm install --save-dev webpack-merge
接下来配置文件也要分开写了,dev +base or pro + base
webpack.common.js
const webpack = require('webpack');
const path = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry:{
app:"./src/index.js",
},
output:{
path:path.resolve(__dirname,"dist"),
filename:"[name].bundle.js"
},
module:{
rules:[
{
test:/.css$/,
use:["style-loader","css-loader"]
},
{
test:/\.(png|jpg|gif|svg)$/,
use:["file-loader"]
}
]
},
plugins:[
new CleanWebpackPlugin(["dist"]) // 先清除dist后再去构建
,
new HtmlWebpackPlugin({ // 会自动在dist目录构建一个包含所有依赖的html,默认名字叫index.html
title:"product environment",
})
]
}
webpack.dev.js ,这里文档里面是有个大坑的,不信你启动webpack.dev.js的时候,
报错了,应该如下:
const merge = require("webpack-merge"); // 用于合并配置,有很多其它功能
const common = require("./webpack.common.js")
module.exports = merge(common,{
devtool:"inline-source-map", // 在运行时生成一个原文件便于调试的
devServer:{
contentBase:"./dist", // 告诉webpack-dev-server, 开户的调试用的本地服务localhost:8080 可用的本地目录是dist
hot:true // 是否进行热更新,配合 plugins:[ new webpack.NamedModulesPlugin() // 给模块命名 ,new webpack.HotModuleReplacementPlugin() // 热模块更新]
},
plugins:[
new webpack.NamedModulesPlugin() // 模块热替换
,new webpack.HotModuleReplacementPlugin() // 模块热替换
]
})
快看图吧!心血呀,一步步的给你们踩坑 !^_^
webpack.prod.js
const merge = require("webpack-merge");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin")
const common = require("./webpack.common.js")
module.exports = merge(common,{
plugins:[
new UglifyJSPlugin()
]
})
重新配置一下NPM Scripts
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.prod.js",
"watch": "webpack --watch",
"dev": "webpack-dev-server --open --config webpack.dev.js"
},
优化:给生产环境也配置source map
// 我们鼓励你在生产环境中启用 source map,因为它们对调试源码(debug)和运行基准测试(benchmark tests)很有帮助。虽然有如此强大的功能,然而还是应该针对生成环境用途,选择一个构建快速的推荐配置(具体细节请查看 devtool
)。对于本指南,我们将在生产环境中使用 source-map
选项,而不是我们在开发环境中用到的 inline-source-map
:
修改一下webpack.prod.js
const merge = require("webpack-merge");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin")
const common = require("./webpack.common.js")
module.exports = merge(common,{
devtool:"source-map", // 生产环境也要配置一下便于调试原码
plugins:[ new UglifyJSPlugin({ sourceMap:true // }) ]
})
指定环境
许多 library 将通过与 process.env.NODE_ENV
环境变量关联,以决定 library 中应该引用哪些内容。例如,当不处于生产环境中时,某些 library 为了使调试变得容易,可能会添加额外的日志记录(log)和测试(test)。其实,当使用 process.env.NODE_ENV === 'production'
时,一些 library 可能针对具体用户的环境进行代码优化,从而删除或添加一些重要代码。我们可以使用 webpack 内置的 DefinePlugin
为所有的依赖定义这个变量:
webpack.prod.js 更改
const webpack = require("webpack")
const merge = require("webpack-merge");
const UglifyJSPlugin = require("uglifyjs-webpack-plugin")
const common = require("./webpack.common.js")
module.exports = merge(common,{
devtool:"source-map",
plugins:[
new UglifyJSPlugin({
sourceMap:true
})
,new webpack.DefinePlugin({ // 改动
"process.env.NODE_ENV":JSON.stringify('production')
})
]
})
CLI 替代选项(建议读一读哦)
以上描述也可以通过命令行实现。例如,--optimize-minimize
标记将在后台引用 UglifyJSPlugin
。和以上描述的 DefinePlugin
实例相同,--define process.env.NODE_ENV="'production'"
也会做同样的事情。并且,webpack -p
将自动地调用上述这些标记,从而调用需要引入的插件。
这些简便方式虽然都很不错,但是我们通常建议只使用配置方式,因为在这两种场景中下,配置方式能够更好地帮助你了解自己正在做的事情。配置方式还可以让你更方便地控制这两个插件中的其他选项。
10.代码分离,去除各个模块代码重复的部分(用webpack自带的方法)
我们多个入口,默认打包出来的会包含重复的模块。如,两个文件都引用了jquery,则,打包出来两个文件里都包含了jquery.
src:里面两个用来测试的文件都引入一个相同的包如 import _ from "lodash"
输入输出不变:还是一般的多入口
entry:{
app:"./src/index.js"
// ,
// another:'./src/another_module.js' // 多入口,也意味着多输出
},
output:{
path:path.resolve(__dirname,"dist"),
filename:"[name].bundle.js"
//,
// 决定非入口chunk的名称,动态导入的包就是一个非入口chunk,
// 如 return import(/* webpackChunckName: lodash */"lodash").then(_=>{}).catch(e=>{})
//chunkFilename:"[name].bundle.js"
}
webpack4+,不支持在plugins里配置
new webpack.optimize.CommonsChunkPlugin({
+ name: 'common' // 指定公共 bundle 的名称。
+ })
解决办法是在与plugins平级的地方:
optimization:{
splitChunks: {
cacheGroups: {
commons: {
test: /node_modules/,
name: "vendors",
chunks: "all"
}
}
},
runtimeChunk:true
}
运行结果如下:
方法二:动态导入(dynamic imports),我只讲重点,其它的自己看官方文档
原理:import()
调用会在内部用到 promises。如果在旧有版本浏览器中使用 import()
,记得使用 一个 polyfill 库(例如 es6-promise 或 promise-polyfill),来 shim Promise
。
请看index.js 新写法:
import "./style.css"
function component() { return import(/* webpackChunkName: "lodash" */"lodash" ).then(_=>{
var element = document.createElement('div');
element.innerHTML = _.join(["我是动态引入的" ],"lodash component 哦!");
return element;
}).catch(error => "'An error occurred while loading the lodash component,haha tangcc'")
} component().then(element =>{ document.body.appendChild(element); })
非入口chunk的名称,:webpack.config.js 中的output配置一下
entry:{
app:"./src/index.js"
// ,
// another:'./src/another_module.js' // 多入口,也意味着多输出
},
output:{
path:path.resolve(__dirname,"dist"),
filename:"[name].bundle.js"
,
// 决定非入口chunk的名称,动态导入的包就是一个非入口chunk,
// 如 return import(/* webpackChunckName: lodash */"lodash").then(_=>{}).catch(e=>{})
chunkFilename:"[name].bundle.js"
},
结果:ok^_^(已测过)
这一小节还差分析检查模块就完结了,bundle 分析(bundle analysis) -- 我没有研究,先跳过去先。。。^_^
如果我们以分离代码作为开始,那么就以检查模块作为结束,分析输出结果是很有用处的。官方分析工具 是一个好的初始选择。下面是一些社区支持(community-supported)的可选工具:
- webpack-chart: webpack 数据交互饼图。
- webpack-visualizer: 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
- webpack-bundle-analyzer: 一款分析 bundle 内容的插件及 CLI 工具,以便捷的、交互式、可缩放的树状图形式展现给用户。
11.懒加载(按需加载) ,当用户点击时才去加载对应的模块,难点在于如何去分模块(未研究)
新建print.js文件的时候,一定要“注意”当调用 ES6 模块的 import()
方法(引入模块)时,必须指向模块的 .default
值,因为它才是 promise 被处理后返回的实际的 module
对象。
print.js
export default function printMe(){
console.log("是谁点我的?print me")
}
index.js
import _ from "lodash" // 这是静态导入
import "./style.css"
function component(){
var element = document.createElement('div');
var button = document.createElement("button") ;
button.innerHTML = "click me print ...";
button.onclick = e => import(/* webpackChunkName:"print" */"./print-edit1.js").then(pExport => pExport.default()).catch(err =>{
console.log("import component encounter some err")
});
element.appendChild(button);
// return import(/* webpackChunkName: "lodash" */"lodash" ).then(_=>{
// var element = document.createElement('div');
// element.innerHTML = _.join(["我是动态引入的" ],"lodash component 哦!");
// return element;
// }).catch(error => "'An error occurred while loading the lodash component,haha tangcc'")
document.body.appendChild(element);
}
component();
配置不用改,有一点点问题就是 chunkFilename:"[name].bundle.js" 命名没有成功, 打包出来的名字竟然是用了id.bundle.js。
entry:{
app:"./src/index.js"
// ,
// another:'./src/another_module.js' // 多入口,也意味着多输出
},
output:{
path:path.resolve(__dirname,"dist"),
filename:"[name].bundle.js"
,
// 决定非入口chunk的名称,动态导入的包就是一个非入口chunk,
// 如 return import(/* webpackChunckName: lodash */"lodash").then(_=>{}).catch(e=>{})
chunkFilename:"[name].bundle.js"
},
又完成了一小节。
12.缓存(概念自己去看官网了^_^)
此指南的重点在于"通过必要的配置",以确保 webpack 编译生成的文件能够被客户端缓存,而在文件内容变化后,能够请求到新的文件。
输出文件的文件名(Output Filenames),用chunkhash命名
其实非常简单啦: filename:chunkhash
output:{
path:path.resolve(__dirname,"dist")
,
filename: "[name].[chunkhash].js"
},
提取模板(Extracting Boilerplate样板文件)
小于webpack4.0用以下两个配置可以实现公共代码的提取。
entry:{
main:"./src/index.js",
vendor:["lodash"]
},
plugins:[
new CleanWebpackPlugin(["dist"]) // 先清除dist后再去构建
,
new HtmlWebpackPlugin({ // 会自动在dist目录构建一个包含所有依赖的html,默认名字叫index.html
title:"product environment",
})
,
// 代码分离时,去除重复模块,不用装 ,第10点 代码分离
new webpack.optimize.CommonsChunkPlugin({ // webpack4 废弃了
name:'common' // 把两个入口重复的chunk提出来,以common.bundle.js的名字打包
})
]
如果是webpack4.0会报以下错误了,
Error: webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead.
解决办法:
plugins:[ // 略
new CleanWebpackPlugin(["dist"]) // 先清除dist后再去构建
,
new HtmlWebpackPlugin({ // 会自动在dist目录构建一个包含所有依赖的html,默认名字叫index.html
title:"product environment",
})
]
,
// 关键代码
optimization:{ // 代码分离时,去除重复模块,不用装 ,第10点 代码分离// 把两个入口重复的chunk提出来,以vendors.bundle.js的名字打包
splitChunks: {
cacheGroups: {
commons: {
test: /node_modules/,
name: "vendors----",
chunks: "all"
}
}
},
runtimeChunk:true
}
模块标识符(Module Identifiers)
主要是控制chunkhash的变化,理想状态下,只有相关文件改动,对应的chunkhash才能变
而 vendor没有改变但它
的 hash 发生变化,所以我们要处理。可以使用两个插件来解决这个问题。
第一个插件是 NamedModulesPlugin
,将使用模块的路径,而不是数字标识符。虽然此插件有助于在开发过程中输出结果的可读性,然而执行时间会长一些。
第二个选择是使用 webpack.HashedModuleIdsPlugin()
,推荐用于生产环境构建.
实际上,我用下面的配置,打包出的vendors文件的chunkhash也不会乱变。
optimization:{ // 代码分离时,去除重复模块,不用装 ,第10点 代码分离// 把两个入口重复的chunk提出来,以vendors.bundle.js的名字打包
splitChunks: {
cacheGroups: {
commons: {
test: /node_modules/,
name: "vendors----", //会提取出各模块的公共代码,自动命名按 output.chunkFilename 的规则 ,这个文件一般比较大>=70K。
chunks: "all"
}
}
},
runtimeChunk:false
}
这一节过了^_^!
13.创建library(除了打包应用程序代码,webpack 还可以用于打包 JavaScript library--就是自己写一个js库发到npm上去)
可能你看了官方的文档,还是不会操作呀,下面请按步骤。
1.按照文档把要发布的代码写好
2.给要发布的代码写一个README.md 文件,发布成功后这个文件会出现在你的包的描述说明中。
.md 文件的写法参考学习文档
https://www.cnblogs.com/liugang-vip/p/6337580.html
.md 文件的在线测试工具
http://tool.oschina.net/markdown/
3.进入项目目录,然后 npm publish (一定是npm 不要用淘宝的cnpm)
4.更新包(其实就是重新发布,在package.json )
a.改:"version":"1.0.1" 或者 npm version 1.0.1
b.npm publish
如果有报错请参考:
首先要设置一个版本号
npm社区版本号规则采用的是semver(语义化版本),主要规则版本格式:主版本号.次版本号.修订号,版本号递增规则如下:
- 主版本号:当你做了不兼容的 API 修改,
- 次版本号:当你做了向下兼容的功能性新增,
- 修订号:当你做了向下兼容的问题修正。
先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。
$ npm publish
这里有时候会遇到几个问题
问题1:
npm ERR! no_perms Private mode enable, only admin can publish this module:
这里注意的是因为国内网络问题,许多小伙伴把npm的镜像代理到淘宝或者别的地方了,这里要设置回原来的镜像。
$ npm config set registry=http://registry.npmjs.org
问题2:
npm ERR! you do not have permission to publish "your module name". Are you logged in as the correct user?
提示没有权限,其实就是你的module名
在npm
上已经被占用啦,这时候你就去需要去npm
搜索你的模块名称,如果搜索不到,就可以用,并且把package.json
里的name
修改过来,重新npm publish
,看到如下信息就表示安装完成了,songpackage
就是我的模块名。
作者:郝小淞
链接:https://www.jianshu.com/p/d9fb02a891d9
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
ok,过了!