如果没有看过webpack相关的东西,先看看webpack 你看我就够了(一),webpack 你看我就够了 (二)。
webpack development server
webpack development server 是一个webpack可选的本地开发的server。它通过nodejs的express 来起一个server提供静态文件服务,同时它根据配置信息(xxx.config.js)来打包资源,存在内存中,同时当你的代码发生改变的时候,它还可以刷新你的浏览器。它是一个单独的npm module,通过npm install webpack-dev-server --save-dev
来给项目安装依赖。
webpack dev server可以通过webpack.config.js的devServer选项来配置,具体配置包括:
- contentBase: 默认webpack dev server是从项目的根目录提供服务,如果要从不同的目录提供服务,可以通过contentBase来配置,比如rails中可以把contentBase配置成'./public'。
- port: 默认webpack是用 8080端口起的,通过port可以配成其他的端口。
- inline: 设置为true,代码有变化,浏览器端刷新。
- colors: 这个是当server跑的时候,terminal输出带颜色。
- historyApiFallback: 这个是干嘛用的嘞?对于单页面程序,浏览器的brower histroy可以设置成html5 history api或者hash,而设置为html5 api的,如果刷新浏览器会出现404 not found,原因是它通过这个路径(比如: /activities/2/ques/2)来访问后台,所以会出现404,而把historyApiFallback设置为true那么所有的路径都执行index.html。
利用html5的history,生产环境的nginx可以这么配置,可以参考下
location / {
expires -1; add_header Pragma "no-cache";
add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
root /var/web;
try_files $uri $uri/ /index.html =404;
}
现在,我们可以把我们的小项目的 webpack.config.js重新配置一下,如下:
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {
path: __dirname + '/public',
filename: 'bundle.js'
},
devServer: {
contentBase: './public',
color: true,
historyApiFallback: true,
inline: true
}
}
现在不用跑webpack的命令,而直接跑webpack-dev-server
这个命令就好了。如果只是安装了本地的依赖关系,那么就是执行./node_modules/.bin/webpack-dev-server
好了。
效果如图:
现在你可以把它写在我们的package.json中了。
具体如下:
{
"name": "webpack-sample-project",
"version": "1.0.0",
"description": "Sample webpack project",
"scripts": {
"start": "node_modules/.bin/webpack-dev-server --progress"
},
"author": "Cássio Zen",
"license": "ISC",
"devDependencies": {
"webpack": "^1.12.9",
"webpack-dev-server": "^1.14.0"
}
}
progress命令行选项是用于显示build的进度
loaders
这个东东是webpack最显著的特点!!!!!!
webpack通过loader来加载各种各样的资源,不同的资源应用的不同的loader ,举个例子:打包es6会用到babel-loader,打包css用到style-loader和css-loader等等。
loaders是通过单独的npm来安装的,然后在webpack.config.js中通过module来配置。loader的配置包括:
- test: 一个正则表达式,用于检测不同的文件的后缀名,然后配置配置不同的loader。
- loader: loader的名字,比如'babel-loader'
- include/exclude: 一个选项来配置哪些目录和文件需要排除掉或者加上
- query: 这个query settings可以用于传递不同的参数给loader
举个例子,让我们把打招呼的信息放到一个单独的json文件中。
首先,我们先安装一个'json-loader',通过npm install json-loader --save-dev
。
下一步,我们来配置webpack.config.js,如下:
module.exports = {
devTool: 'eval-source-map',
entry: _dirname + '/app/main.js',
output: {...},
module: {
loaders: [
{
test: /\.json$/,
loader: "json"
}
]
},
devServer: {
contentBase: './public',
color: true,
historyApiFallback: true,
inline: true
}
}
最后,我们创建一个config.json文件来写一些打招呼需要的信息,如下:
{
"greetText": "Hi there and greeting from JSON!"
}
更新后的Greeter.js,如下:
var config = require('./config.json');
module.exports = function(){
var greet = document.createElement('div');
greet.textContent = config.greetText;
return greet;
}
babel
babel是一个编译javascript的工具,它可以实现:
- 让你用下一代javascript(es6/es7/es2015/es2016)来写代码。
- 可以使用javascript的扩展语法,比如react jsx。
babel是一个单独的工具,但是我们可以通过babel-loader在webpack中应用它。
babel的安装和配置
babel是一个模块化的并且分发到不同的npm modules。核心的功能 babel-core 是通过babel-loader按装来直接使用的。但是,对于一些其他的功能和扩展要另外的安装(最常用的是babel-preset-es2015和babel-preset-react分别用于支持es6和react jsx)。
我们来安装所需的包npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
像其他的laoder一样,可以通过配置文件来配置。下面是更新后的webpack.config.js
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js", output: {...},
module: {
loaders: [
{
test: /\.json$/, loader: "json"
},
{
test: /\.js$/,
exclude: /node_modules/, loader: 'babel',
query: {
presets: ['es2015','react'] }
}
]},
devServer: {
contentBase: "./public",
colors: true,
historyApiFallback: true,
inline: true
}
}
现在我们的项目可以利用es6的属性和react的jsx,现在让我们来给我们的项目引入react和react-dom。
现在重构我们的Greeter.js,如下:
import React, {Component} from 'react';
import config from './config.json';
class Greeter extends Component {
render() {
return (
<div>
{config.greetText}
</div>
);
}
}
export default Greeter;
现在来重构我们的main.js,如下:
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
render(<Greeter/>, document.getElementById('root'));
babel的配置文件
babel可以通过webpack的配置文件直接配置,但是它有很多的配置信息,都放到同一个webpack的配置文件中会使得配置文件不好维护。因为这个原因很多的开发者选择了单独的babel配置文件'.babelrc',来配置bebel的选项等等。
目前我们对babel的配置只是presets,所以我们先把这个配置放到'.babelrc'中吧,后面会一些别的配置信息的。
现在我们的webpack的配置信息如下:
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + '/app/main.js',
output: {...},
module: {
loaders: [
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel'
}
]
},
devServer: {...}
}
去掉了对babel loader的presets配置,那么.babelrc文件如下:
{
"presets": ["react", "es2015"]
}
其他资源(除了javascript)
webpack最大的一个特点就是能把各种各样的文件当做module来对待调用。这些资源文件包括 js,css, font, image 等等。webpack能够通过@import和url值等相应的处理css文件的依赖关系,编译打包。
样式表
webpack提供了css-loader和style-loader来处理样式表。不同的loader处理不同的任务,其中css-loader处理@import和url值来解决他们的依赖关系,然后style-loader把这些计算后的样式表加到页面上。总结来说呢,就是这两个loader共同实现了把样式表嵌入到webpack的js bundle中。
现在把这两个loader添加到我们的小项目中。
首先通过npm来安装 npm install --save-dev style-loader css-loader
然后更新我们的webpack配置文件
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + '/app/main.js',
output: {...},
module: {
loaders: [
{test: /\.json$/, loader: "json"},
{test: /\.js$/, exclude: /node_modules/, loader: 'babel'},
{test: /\.css$/, loader: 'style!css'}
]
},
devServer: {...}
}
注意 loader中的(!)这个符号用于连接不同的loader的,在这里就是.css文件要被style-loader和css-loader同时处理。
现在呢,我们来新建一个main.css来写一些基本的样式,用于我们的小程序。
main.css如下:
html {
box-sizing: border-box;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
margin: 0;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h1, h2, h3, h4, h5, h6, p, ul {
margin: 0;
padding: 0;
}
最后记住我们的webpack是起始于一个或多个entry point文件的,然后分析依赖关系等等,再进行打包。这里我们的entry point文件是 main.js,所以我们要在main.js中引用main.css就像引用js一样引用,这个是不是很酷??,好了main.js加上下面那一句。
import './main.css';
css 模块
在过去几年中,javascript发生了很多的变化,添加了很多的语言功能,同时更多更好的前段工具化使得开发更加高效同时建立一些最好的实践(best pratice)例如modules。
模块使得开发者可以把代码分成很多小的单独的单元,然后声明之间的依赖关系。通过好的优化工具,依赖管理,依赖顺序自动会被解决。
但是随着javascript的发展,css还是单片的,全局的,使得开发越来越难以维护和复杂。
最近一个项目叫做'css modules'目的就是把模块化等等的特点带给css。通过css modules,所有的css的classname和animation name都是本地scoped。webpack从一开始就加入了这个项目在css loader中,只是我们需要显示的开启它。有了这个功能,你可以export class来给指定的component。
这样说可能比较抽象,那么我们来把这个功能添加到我们的项目当中。
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js", output: {...},
module: {
loaders: [
{ test: /\.json$/, loader: "json" },
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel' }, {
test: /\.css$/,
loader: 'style!css?modules' }
]
},
devServer: {...}
}
现在我们来创建一个Greeting.css文件,然后在Greeter.js中引用这个文件,应用这个css文件中定义的class,具体代码如下:
Greeting.css代码:
.root {
background-color: #eee,
padding: 10px;
border: 3px solid #ccc;
}
那么我们的Greeting.js代码变更如下:
import React, {Component} from 'react';
import config from './config.json';
import styles from './Greeter.css';
class Greeter extends Component{
render() {
return (
<div className={styles.root}>
{config.greetText} </div>
);
}
}
export default Greeter
注意到css怎么被一个变量引用,然后应用的一个jsx的element上的。
这个是不是很酷,一些通用的class可以这样被本地性的引用,不会受到命名冲撞的影响。
css modules是一个比较大的主题,而且还有很多的功能实现,想了解更多可以查看这个。
css的预处理器
css的预处理比如sass less stylus 都扩展扩展了css的语法。他们让我们可以利用一些css中没有的功能来写css,比如变量,函数,嵌套,mixins等等。其实从概念上 es6/coffeescript 转译成 js和sass等转译成css是相似的。
正如你想象的那样,在webpack中可以利用相应的loader来处理这些预编译。
- less loader https://github.com/webpack/less-loader
- sass loader https://github.com/jtangelder/sass-loader
- stylus loader https://github.com/shama/stylus-loader
一个新的趋势更加宽松的css工作流程是通过应用PostCSS来实现的。不是通过一个完整的,固定的css语言,PostCSS是一个css转译工具。通过连接不同的插件,来应用不用的转译到你的css文件。你可以了解更多通过这个。
这里我们通过PostCSS和autoprefixer插件来举例子,其中autoprefixer是给我们的css自动添加浏览器供应商前缀。
首先安装这些包, npm install --save-dev postcss-loader autoprefixer
现在我们来重新配置webpack,如下:
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/app/main.js",
output: {...},
module: {
loaders: [
{ test: /\.json$/, loader: "json" },
{ test: /\.js$/, exclude: /node_modules/, loader: 'babel' },
{
test: /\.css$/,
loader: 'style!css?modules!postcss'
} ]
},
postcss: [ require('autoprefixer')],
devServer: {...}
}
下一个系列我们会了解一些webpack插件的知识,和生产环境相关的配置。