看过许多关于webpack的文章,最近终于有时间(实习+学校课程设计)去解决之前mac终端npm command not found的问题,再去使用webpack工具啦~
前提
- 使用webpack的前提条件:我的电脑是mac,先下载node-XXX.pkg。在下载完成之后,我输入
node -v
,并没有显示版本号,而是node command not found
。很多时候我都是卡在mac的文件夹权限问题上,或是PATH配置问题。 - 如果你也出现这样的问题,因为node是默认安装在/use/local/bin下,
export PATH=$PATH:/usr/local/bin
来配置PATH。这时候命令不存在的问题就解决啦~
安装
接下来我们就开始一步一步的安装使用webpack。(以下以mac为例)
- 打开”终端”
- 创建一个文件夹
mkdir mydirectory
(默认在padonis下,如需修改路径可以cd进入其它目录)- 进入该文件夹
cd my directory
npm init
- 指定webpack版本号
npm install webpack@2.2.1 --save -dev
注:这里nam install webpack –save -dev默认下载最新版本,我默认下载的是webpack3.0.0,可以通过npm install webpack@2.2.1 --save-dev
指定版本号,推荐1.x版本或2.x版本,很多webpack技术文章都是这些旧版本,而且我在用3.0.0版本的时候独立出css插件部分使用有点问题
如果在你cd的目录里看到node_modules文件夹就说明安装webpack成功了。(根目录下会出现一个node_modules文件夹,npm安装的各种依赖包都会默认安装到这个文件夹下面)
并且多了一个package.json文件,内容就是一堆json数据,基本就是我们刚才在终端里输入的信息
这里你会看到mydirectory文件夹下还有app文件夹、public文件夹和webpack.config.js文件,这些都是需要我们自己创建的(当然文件夹名字你可以自定义)。
app文件夹(自定义):放置我们的开发文件
public文件夹(自定义):就是浏览器执行需要的文件
webpack.config.js文件(自定义 必须):webpack的配置文件,稍后会详细说明
简单入门
在app文件夹下创建一个main.js文件
//main.js
document.write("hello world!");
在app文件夹下创建一个main.js文件
//basic.js
document.write('HELLO,Suoz!');
在app文件夹下创建一个index.html文件
//index.html
<div id="box">
<h2>h1</h2>
<p>webpack</p>
</div>
<script type="text/javascript" src="../public/app.bundle.js"></script>
接下来在webpack.config.js文件下修改配置,这里entry代表指定的入口文件(这个入口文件可以通过require引入其它文件),output代表输出文件(即将所有入口文件指定的资源按照依赖关系打包输出一个输出文件,并放到index.html中引入即可)
//在webpack 2.2.1版本下配置
const webpack = require("webpack");
module.exports = {
context: __dirname,
entry: {
app: ["./app/main.js"],
},
output: {
path: __dirname + "/public",
filename: "[name].bundle.js",
},
};
__dirname : 根目录(这里的根目录是mydirectory文件夹)
Webpack 基本做了下面这些事情:
- 从 context 对应的文件夹开始…
- …寻找 entry 里所有的文件名…
- …然后读取它们的内容。在解析代码时,每一个通过 import(ES6) 或 require() (Node) 引入的依赖都会被打包到最终的构建结果当中。它会接着搜索 那些依赖,以及那些依赖的依赖,直到“依赖树”的叶子节点 — 只打包它所需要的依赖,没有其他的东西。
- 接着,Webpack 将所有东西打包到output.path 对应的文件夹里,使用 output.filename 对应的命名模板来命名( [name] 被 entry里的对象键值所替代)
关于webpack的启动方式:
webpack // 最基本的启动webpack的方法
webpack -w // 提供watch方法;实时进行打包更新
webpack -p // 对打包后的文件进行压缩
webpack -d // 提供source map,方便调式代码
注:webpack3.0.0版本下的输出文件path不能直接是’./public’,否则会出现configuration.output.path: The provided value "./public" is not an absolute path!
的错误
此刻的mydirectory文件夹目录:
输出文件名为bundle.js,由于在./app/index.html中引入了bundle.js,所以打开index.html时,页面如下图:
多个JS输入文件 多个输出JS文件
const webpack = require("webpack");
module.exports = {
context: __dirname,
entry: {
home: ["./home_1.js","./home_2.js"],
events: ["./events.js"],
},
output: {
path: __dirname + "/public",
filename: "[name].bundle.js",
},
};
=>home_1.js和home_2.js文件 输出一个home.bundle.js文件
=>events.js文件 输出一个events.bundle.js文件
CSS文件 css-loader style-loader
你可能注意到甚至在生产构建的结果中,也把 CSS 打包进了 JavaScript 里面,并且 style-loader 手动地将样式写进了 <-head> 中。乍一看这可能有点奇怪,但当你考虑足够多的时候就会慢慢发现这其实是有道理的。你保存了一个头部请求(在某些连接上节省宝贵的时间),并且如果你用 JavaScript 来加载 DOM(通过DOM操作动态创建<-style>,并通过代码将样式添加在<-style>标签中),这么做基本上就消除了它自身的无样式闪屏问题。
前提:需要使用npm命令下载style-loader和css-loader。因为webpack只能识别原生的js模块,而对于css模块、ES6模块等都需要通过loader加载器进行转换,转换成webpack能识别的模块。
//在终端输入
npm install style-loader --save-dev
npm install css-loader --save-dev
下载完成之后,在package.json文件下你会发现自动多了这么一行即代表下载成功
"dependencies": {
"css-loader": "^0.28.4",
"style-loader": "^0.18.2"
}
接下来开始创建相关的文件 并 写入代码
//basic.css
body{
font-size: 40px;
color: red;
}
//main.js
document.write("hello,main.js");
require("./basic.css");
const webpack = require("webpack");
module.exports = {
//...code
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader","css-loader"],
//这些 loader 会以数组逆序运行。这意味着 css-loader 会在 style-loader 之前运行
},
],
},
};
//index.html不变,还是引入app.bundle.js文件
<div id="box">
<h1>hello</h1>
<p>webpack</p>
</div>
<script type="text/javascript" src="../public/app.bundle.js"></script>
总结:把CSS文件放入JS文件中一起打包出一个输出文件,同时在.html也只是引用这个js文件,但是这个文件内部会自动帮我们动态创建一个<-style>标签,并将CSS中的样式通过DOM操作加入<-style>标签内部,无需我们操心。这样也减少了请求文件(JS、CSS)的数量。
注:先执行css-loader将css样式转换为webpack能够识别的模块,再通过style-loader将模块通过JS DOM操作动态创建<-style>标签,并加入<-head>标签内部。因此在后面独立出CSS文件中无需在加入style-loader
index.html显示如图:
SASS sass-loader
安装sass-loader加载器:
npm install sass-loader --save-dev
//basic.scss
body{
font-size: 40px;
& p{
font-size: 20px;
color: #ccc;
}
}
//basic.css
#box{
width: 400px;
height: 400px;
background-color: green;
}
//main.js
document.write("hello,main.js");
require("./basic.scss");
require("./basic.css");
//index.html
<div id="box">
<h1>hello</h1>
<p>webpack</p>
</div>
<script type="text/javascript" src="../public/app.bundle.js">
//然后添加另外一条rules
module.exports = {
// …
module: {
rules: [
{
test: /\.(sass|scss|css)$/,
use: [
"style-loader",
"css-loader",
"sass-loader",
]
},
// other ...
],
},
};
一片片的红海啊。。有点刺眼,我们看看发生了什么,原来是提示没有安装node-sass模块,那么我们继续安装:
npm install node-sass --save-dev
再次执行 webpack -w,就成功啦~
独立出CSS文件
遇到一个bug,在webpack2.X版本下,extract-text-webpack-plugin最好也使用2.X版本。如果不指定版本,可能会导致错误。
npm install extract-text-webpack-plugin@2.0.0-beta.4 --save-dev
const webpack = require("webpack");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
context: __dirname,
entry: {
app: ["./app/main.js"],
output: {
path: __dirname + "/public",
filename: "[name].bundle.js",
},
module: {
rules: [
{
test: /\.css$/,
loader: ExtractTextPlugin.extract({
//注:这里没有style-loader
loader: 'css-loader?importLoaders=1',
}),
},
// …
]
},
plugins: [
new ExtractTextPlugin({
filename: "[name].bundle.css",
allChunks: true,
}),
],
};
而webpack2.2文档是这么定义的,但是使用npm命令下载的插件默认是3.x版本,而且按照官网的也不能实现,如果有大牛知道请告诉我。
SASS+独立出CSS
一开始[“style-loader”,”css-loader”,”sass-loader”]。后来才知道style-loader是为了将样式动态加入head标签内部。在这里再次提醒~~独立出CSS文件千万不要有style-loader
const ExtractTextPlugin = require("extract-text-webpack-plugin");
module: {
rules: [
{
test: /\.(sass|scss|css)$/,
loader: ExtractTextPlugin.extract({
loader: 'css-loader!sass-loader?importLoaders=1',
}),
},
// …
]
},
plugins: [
new ExtractTextPlugin({
filename: "[name].bundle.css",
allChunks: true,
}),
],
ECMAScript6转换器babel
都知道ES6代码在大部分浏览器下不能直接运行。所以需要通过babel转换器转换为ES5代码。而webpack提供了这个功能。
npm install babel-loader babel-core babel-preset-es2015
// ...code
module: {
rules: [
{
test: /\.js$/,
use: [{
loader: "babel-loader",
options: { presets: ["es2015"] }
}],
},
// Loaders for other file types can go here
],
},
//basic.js(es6代码)
let arr = [1,2];
let [a,b] = arr;
document.write(a + ":" + b);
//入口文件main.js
require("./basic.js");
开发服务器 webpack-dev-server
实际上 Webpack 有它自己的开发服务器,所以无论你正在开发一个静态网站,或者只是正在原型化前端阶段,这个服务器都是完美可用的。想要运行它,只需要在 webpack.config.js 里添加一个 devServer 对象:
module.exports = {
context: __dirname + "/app",
entry: {
app: "./main.js",
},
output: {
filename: "[name].bundle.js",
path: __dirname + "/public/assets",
publicPath: "/assets", // New
},
devServer: {
contentBase: __dirname + "/app", // New
},
};
现在新建一个 app/index.html 文件,加入下面这行:
<script src="/assets/app.bundle.js"></script>
然后在命令行中,运行:
webpack-dev-server
服务器现在就运行在了 localhost:8080 上。注意 script 标签中的 /assets 对应的是 output.publicPath 的值 - 可以随便填成你想要的命名(如果需要一个 CDN,这就很有用了)。
当你更改 JavaScript 代码的时候,Webpack 就会实时更新页面而无需手动刷新浏览器。但是, 任何对 webpack.config.js 的更改都需要重启服务器 才可以生效。
分离出公共代码
如果你正在将应用拆解,打包成多个 output 的话(如果应用的某部分有大量不需要提前加载的 JS 的话,这样做会很有用),那么在这些文件里就有可能出现重复的代码,因为在解决依赖问题的时候它们是互相不干预的。幸好,Webpack 有一个内建插件 CommonsChunk 来处理这个问题:
const CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "commons",
filename: "commons.js",
minChunks: 2,
}),
],
CSS Module 作用域
都知道CSS是全局定义的,如果一个开发者定义一个类.style,并为它设置样式。另一个开发者定义同一个类.style。此时的后面定义中同名属性会覆盖前面的。如果想解决这个问题,可以使用webpack中css-loader设置属性。
//basic.css
body{
background-color: blue;
}
.class1{
color: green;
}
//basic_2.css
body{
background-color: white;
}
.class1{
color: pink;
}
//component.js
export default(class1,class2) => {
var box = document.getElementById("box");
box.className = class1;
var p = document.getElementById("p");
p.className = class2;
return;
}
//main.js
import component from "./component.js";
import basic1 from "./basic.css";
import basic2 from "./basic_2.css";
component(basic1.class1,basic2.class1);
//webpack.config.js
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader',{
loader: 'css-loader',
//为css-loader设置属性
options: {
modules: true,
},
}],
},
]
},
自动引入JS输出文件
通过webpack.config.js中output输出的文件,每次你都要在.html文件内部引入,如果你觉得麻烦,在webpack内部提供了html-webpack-plugin插件为你自动引入。
const webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
context: __dirname+"/app",
entry: {
app: ["./main.js"],
demo : ["./demo.js"],
},
output: {
path: __dirname + "/public",
filename: "[name].bundle.js",
},
plugins: [
new HtmlWebpackPlugin({
title: '首页',
template:__dirname+'/app/index.html', // 文件模板
filename: __dirname + '/public/index.html', // 生成的文件名
chunks:["app"], //引入的JS模块
}),
new HtmlWebpackPlugin({
title: '购物车',
template:__dirname+'/app/index.html', // 文件模板
filename: __dirname + '/public/basic.html', // 生成的文件名
chunks:["demo","app"], //引入的JS模块
//chunks 选项的作用主要是针对多入口(entry)文件。当你有多个入口文件的时候,对应就会生成多个编译后的 js 文件。那么 chunks 选项就可以决定是否都使用这些生成的 js 文件。chunks 默认会在生成的 html 文件中引用所有的 js 文件,当然你也可以指定引入哪些特定的 js 文件。
}),
]
};
到这儿你会发现public目录下的index.html和basic.html都引用了app目录下的index.html。而前者在body结束标签前面引入了app.bundle.js输出文件,后者不仅引入了demo.bundle.js,还引入了app.bundle.js。
./app/index.html
./publuc/index.html
./public/basic.html
CSS3属性添加私有前缀 postcss-loader
nam install postcss-loader --save -dev
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
{
loader: "postcss-loader", // 添加浏览器前缀
options: {
plugins: function () {
return [
require('autoprefixer')
]
}
}
},
],
},
]
},
body{
display: flex;
}
.class1{
color: green;
}
=>