什么是webpack打包,我简单地理解为将其他一系列语法(react,JSX,ES6)重新构建成JS。为什么这么做?是因为前端发展到模块化概念,状态驱动概念,有了更新的标准,但这些优秀的概念并没有被标准化,打包就是为了翻译这些新语法使其适应于当前标准,可以理解为JS的浏览器兼容性问题(大家回忆一下这个:客户死活不愿意用google浏览器之IE浏览器兼容性)。
1、创建你的项目目录,我是在桌面创建了一个文件夹envtest,然后在此文件夹里面打开powershell(cmd),npm init
:这个命令用于创建一个package.json。
2、npm install webpack --save-dev; npm install webpack -g
全局和本地下载webpack,全局下载webpack是为了在cmd命令工具里面可以用webpack命令,这个命令就是构建命令。本地安装是为了可以在项目里面引用。
由于外网太慢了,所以使用淘宝镜像 :
npm –registry https://registry.npm.taobao.org install [package] –save-dev
此方法是临时使用,并不改变npm的registry配置。
另外一种方法是在nodejs安装目录->node_modules->npm->npmrc文件,打开后换行添加registry = https://registry.npm.taobao.org。重新打开cmd命令窗口,输入npm get registry查看当前设置
3、 在根目录下建立webpack.config.js ,这个是webpack构建项目默认配置文件,也可以指定别的配置文件,比如webpack.dist.config.js,不过运行的时候需要假设config参数,webpack --config webpack.dist.config.js.
4、配置webpack.config.js:
var webpack = require('webpack');
module.exports = {
entry: {
bundle: './index.js' (1)
},
output: {
path: __dirname + '/build/', (2)
filename: '[name].js' (3)
}
}
(1) entry就是入口文件,即我要把哪些文件进行构建,这里一般是你的项目入口文件。index.js就是项目入口文件,webpack会从它开始,一个个找到与它相关的文件,然后进行编译, 注意这里的路径,一定不要写成 index.js,要写成./index.js,相对于webpack.config.js
(2) output就是输出的js目录路径,一般都填个绝对路径 (__dirname指的是webpack.config.js所在的目录)。
(3) 输出文件名称[name]指代的是entry中的键名bundle(bundle.js)
5、在相对于webpack.config.js的路径建立index.js,输入var x = 10 在webpack.config.js所在目录,cmd执行webpack
,构建完成后,此目录下生成了build文件假,里面存放了bundle.js
1、本地安装后会在当前目录下载一个node_modules文件夹,当在项目里用require(‘webpack’),它会依次找当前目录下的node_module,然后再找它的父文件夹的node_modules,最后到根目录去寻找node_modules,比如我在我的F盘下面的Project目录的项目里用了require, 那么它的寻找路径就为[ ‘F:\Project\node_modules’,’F:\node_modules’ ],如果在 我的电脑->环境变量 -> 系统变量 -> 增加一个NODE_PATH,如果require没有找到相应模块,那么最后搜索NODE_PATH,我把它设置为我的全局安装的模块路径。那么也可不用本地下载,requie也能找到。【全局安装的模块路径】:打开nodejs安装目录/node_modules/npm/npmrc这个文件,里面就是全局安装的路径,我的是prefix=${APPDATA}\npm,在cmd里面echo %APPDATA%就能看到路径了我的电脑里面是C:\Users\Administrator\AppData\Roaming\npm\node_modules。
2、npm -v 的版本为3以上的话,下载包的依赖包是平行的,这样的好处就是文件夹之间不会层层嵌套,层层嵌套的文件夹会有不可复制且难以删除的麻烦,node_modules可复制就代表在别的项目下不用重新下载)
3、filename也可以写成[name].[hash].js,这时候[hash]是根据index.js内容生成,如果内容变化,hash值也会变化,此作用是为了强制更新浏览器缓存
webpack的作用是构建,以reactJS为例,你要用reactJS框架,而且还要用JSX语法,那就要引入相应的框架。我们直接在需要用到react的js文件里面用var react = require(“react”),或者 import React from ‘react’( es6的语法)。
1、 在根目录里面建立一个main.html, script引用bundle.js(这个就是最后构建好的js文件)
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MAINtitle>
head>
<body>
<div id='main'>div>
<script src="./build/bundle.js">script>
body>
html>
2、 在项目目录下载react,react-dom插件npm install react react-dom --save-dev
,然后为了解析react,我们需要babel用于解析es6语法,和针对React的所有的预设插件
npm install babel babel-loader babel-core babel-preset-es2015 --save-dev
npm install babel-preset-react --save-dev
npm install babel-preset-stage-0 --save-dev
babel-preset-stage-0这个包含最新语法,stage-n n越在后面,它就越规范,有些新特性就没有,如果想用所有新特性,直接stage-0。
babel-preset-react是react语法包,这个包,是专门作为react的优化,让你在代码中可以使用React ES6 classes的写法和JSX语法。
用淘宝镜像下载得很快
3、实验es6语法,修改index.js内容如下:
{
let s = 10;
setTimeout(() => {
alert(s)
},1000)
}
webpack.config.js增加一个module配置项,如下:
var webpack = require('webpack');
module.exports = {
entry: {
bundle: './index.js',
},
output: {
path: __dirname + '/build/',
filename: '[name].js'
},
module: {
loaders: [ (1)
{
test: /\.js$/,
loader:'babel-loader', (2)
exclude: /node_modules/
}
]
}
}
(1):loaders就是构建器,代表哪些文件用哪些构建器,这里的意思是把(test: /.js$/) 以.js为结尾的文件用’babel’插件进行构造但是构造过程/node_modules/这个文件里面的.js结尾文件不进行处理。 loaders是一个数组, 里面可以指定对css, scss, png等文件的构建处理。
(2):这是1年前做的笔记,当时这里loader填写的是babel,但是编译时报错
The node API for babel has been moved to babel-core,
这一年里面nodeJS已经升级到8.9.3,npm 是5.5.1 ,webpack的版本都到3.3.10.0,简直太快了。在stackoverflow里面找到解决方法,换成babel-loader
4、webpack构建,完成后打开build文件夹查看是否正确编译,然后在浏览器中打开main.html,1秒后弹出10
5、下面我们实验react,此时需要在项目根目录创建一个.babelrc文件,和package.json同级, 内容如下:
{
presets: ['es2015','stage-0','react']
}
表示bable构建器接受stage-0,react的语法并将其构建成es5语法。(个人如此理解, 有更正确的理解请指出),具体配置可参照http://babeldev.dan.cx/docs/usage/babelrc/,此文件是比较关键。
6、修改index.js的内容变成:
import React,{Component} from 'react'
import { render } from 'react-dom'
class Box extends React.Component{
render(){
return (
just pretend i am a box
)
}
}
render(
,
document.getElementById("main")
)
7、执行构建命令webpack, 最后在浏览器中打开main.html, 会发现id=main的div下面有h1元素了。
一年前React.createClass还能用,现在废弃了,直接就报错了。简直了……,介绍一个react中文网站给大家参阅下https://doc.react-china.org/docs/hello-world.html
8、更进一步,高级的es6,构造器和asycn函数, 这2二个是异步控制神器, 具体请访问http://es6.ruanyifeng.com/,,着重看‘Generator’, ’Promie‘,‘异步操作和Async函数‘三个章节,我们将index.js改写如下
function* gen(){
var x = yield 10;
var y = yield 20;
var z = x + y;
return z
}
let g = gen()
let x, y, z;
x = g.next().value;
y = g.next(x).value;
z = g.next(y).value;
alert(z)
构建成功后,main.html控制台却报Uncaught ReferenceError: regeneratorRuntime is not defined
这是因为我们少了一个垫片babel-polyfill。
npm install babel-polyfill --save-dev
然后在index.js里面引入
import "babel-polyfill"
function* gen(){
var x = yield 10;
var y = yield 20;
var z = x + y;
return z
}
let g = gen()
let x, y, z;
x = g.next().value;
y = g.next(x).value;
z = g.next(y).value;
alert(z)
//此时会弹出30
如果入口文件index.js开头引入import "babel-polyfill"
,但是用到ES7的async函数仍然报错:
regeneratorRuntime is not defined。
试试将bable-polyfill放到webpack.config.js中,入口文件的import “babel-polyfill”可以删掉
...
entry: {
bundle: ['babel-polyfill','./index.js']
},
...
在项目入口文件的开头即index.js里面引用它,然后通过入口文件import进来的的其他业务逻辑文件也可以被babel-polyfill支持使得生成器函数和异步函数可用。我们可以理为一开始就加载了一个组件js库,在这之后加载的js文件都可以用这个库.。
1、在根目录下创建文件夹css, 在css文件夹里面创建一个main.css
h1{color:red}
2、在webpack.config.js的loaders增加对css的处理,修改为:
var webpack = require('webpack');
module.exports = {
entry: {
bundle: './index.js',
},
output: {
path: __dirname + '/build/',
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.js$/,
loader:'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
loader: "style-loader!css-loader" (1)
}
]
}
}
(1):将.css文件先经过css-loader处理,然后再经过style-loader处理,style-loader就是在webpack运行时遇到require(“main.css”)时,把这个文件的样式用style包裹插入到index.html的head标签下,css-loader的作用就是把url和@import翻译成requier。例如:url(image.png) => require(“./image.png”).
3、修改index.js的内容如下:
import "babel-polyfill"
import React,{Component} from 'react'
import { render } from 'react-dom'
require("./css/main.css") (1)
class Box extends React.Component{
render(){
return (
just pretend i am a box
)
}
}
render(
,
document.getElementById("main")
)
(1):引入main.css,这里要用require语法不能用import,因为import是es6语法,import的是js文件,且js文件中需要定义export。
4、在命了行输入webpack进行构建,然后打开main.html,会看到样式生效,通过元素选择器去查看dom元素,发现html中多了个style标签。
1、npm install url-loader file-loader --save-dev
, 并且在根目录建立images文件夹,这里是作为开发时存放图片资源
2、修改main.css如下:
h1{color:green;height:200px;background:url('../images/test/test.png')}
3、修改webpack.config.js的配置如下
var webpack = require('webpack');
module.exports = {
entry: {
bundle: './index.js',
},
output: {
path: __dirname + '/build/',
filename: '[name].js',
publicPath: "./build/" (1)
},
module: {
loaders: [
{
test: /\.js$/,
loader:'babel',
exclude: /node_modules/
},
{
test: /\.css$/,
loader: "style-loader!css-loader" (1)
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192&name=distImg/[name].[ext]' (2)
}
]
}
}
(1):publicPath指定的路径会在打包的时候,将url-loader处理过后的文件进行重新定向。css里面的url形式如果是相对路径形式:url(../images/test/test.png),且图片大于8192K,构建时指定了publicPath,构建后在浏览器中打开main.html用dom探查器查看style标签里面的css,图片url被替换成了publicPath/[name],name就是打包图片时,指定的name(distImg/[name].[ext]),所以这里应该看到url(./build/distImg/test.png),”./build/”是publicPath,”distImg/test.png“是name。
(2):url-loader会让小于8KB(8129bits)的图片将直接以base64的形式内联在代码中,一般限制小图片转 base64 可以用 url-loader,其他情况都用 file-loader,url-loader应该是file-loader上加了一层过滤。在这里file-loader虽然并未在webpack.config.js中明确指出来,但是在项目过程中发现url-loader好像依赖file-loader,所以file-loader也需要下载 name=distImg/[name].[ext]表示图片会被打包到打包目录下,并建立一个distImg文件夹,打包后的图片在此文件夹下。
在这个例子中,图片路径是envtest/images/test/test.png,main.css里面 用../images/test/test.png来找到图片,构建后在build目录下,产生了distImg/test.png,由于此种方法是将css内嵌到style标签,main.html去找就需要用./build/distImg/test.png,所以publicPath设置为 “./build/distImg”
1、下载插件npm install extract-text-webpack-plugin –save-dev,,extract-text-webpack-plugin用于提取css为单一文件
2、修改webpack.config.js
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin'); (1)
module.exports = {
entry: {
bundle: './index.js'
},
output: {
path: __dirname + '/build/',
filename: '[name].js',
publicPath: './' (2)
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("css-loader”,”style-loader") (3)
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192&name=distImg/[name].[ext]'
}
]
},
plugins:[
new ExtractTextPlugin('prefixer_main.css', { (4)
disable: false,
allowChunks: true
})
]
}
(1):引入ExtractTextPlugin
(2):由于css打包成一个单独文件在build目录,publicPath需要改变下
(3):用ExtractTextPlugin.extract方法将css由css-loader处理后提取出出来,如果提取失败,则用style-loader处理,即以内联样式插入到index.html中。以前是 ExtractTextPlugin.extract(“style-loader”,”css-loader”)现在需要写成ExtractTextPlugin.extract(“css-loader”,”style-loader”),否则报错:
Module build failed: ModuleParseError:Module parse failed: Unexpected token (1:2)
You may need an appropriate loader to handle this file type
(4):用ExtractTextPlugin将css提取为perefixer_main.css,并放在output.path下。
它将从每一个用到了require(“*.css”)的entry chunks文件中抽离出css到单独的output文件
由于此时css单独提取出来,则在prefixer_main.css需要通过./distImg/test.png才能引用到图片,所以publicPath= ‘./’。
3、修改main.html,在head头部添加
"stylesheet" href="./build/prefixer_main.css" type="text/css" />
4、打开main.html,展示正确,打开prefixer_main.css,看到图片路径变成了url(./distImg/test.png)。
只需要将main.html和build目录部署到生产就可以了,其他文件不用部署
下一篇会介绍如何使用热刷新,提前吐槽下,这个是1年前做的笔记,特地翻出来照着配置一遍,由于版本升级太多做了些修改,其中最坑的修改时webpack和webpack-dev-server的版本适配太奇葩,webpack是3.x.x的话,webpack-dev-server就必须是2.x.x。webpack是4.x.x的话,webpack-dev-server就是3.x.x。这个还是翻了好多帖子才找到的,原贴主人说是在google里面才能找到。
附上此项目git:https://github.com/kiramario/hello-world.git