shushu2013 / shushu2013.github.io Goto Github PK
View Code? Open in Web Editor NEWHexo 博客
Home Page: https://shushu2013.github.io
Hexo 博客
Home Page: https://shushu2013.github.io
这个是跟着上面的资料学习 Webpack 3.X
整理记录下来的学习笔记
Webpack
学习第一步Webpack
系统要求:
NodeJS 版本 5.0.0 以上
npm (国内把 cnpm 也安装上)
编辑器推荐使用 VSCode
新建一个 Web 项目,并初始化
mkdir webpack-demo
cd webpack-demo
npm init // 会生成一个 package.json 文件,包含项目的相关信息
安装 Webpack
到本地项目(之后所有命令都是在新建的 webpack-demo
目录下进行)
cd webpack-demo
// 国内使用 cnpm 下载
cnpm install --dev-save webpack // 命令简写:cnpm i -D webpack
// 安装指定版本
cnpm i -D webpack@<version>
// 卸载
npm uninstall -D webpack
Webpack
官方推荐的 Webpack
配置文件为 webpack.config.js
,并且放在项目根目录下
cd webpack-demo
touch webpack.config.js // linux 命令
// windows 下有 cmd 和 powershell 终端,我的 VSCode 里用的是 powershell 终端
copy nul > test.js // cmd 下创建 test.js 文件
del test.js // cmd 下删除 test.js 文件
new-item test.js -type file // powershell 下创建 test.js 文件
// 简写:new-item test.js -t f
rm test.js // powershell 下删除 test.js 文件
在项目根目录下运行 ./node_modules/.bin/webpack -v
查看 Webpack
版本
在 npm script
里定义 Webpack
相关任务(会优先使用本项目下安装的 Webpack
),定义在项目的 package.json
文件中的 scripts 项中
"scripts": {
"start": "webpack --config webpack.config.js"
}
之后可以执行: npm run start
来运行 start
这个任务
一般我们开发环境
的源码放在项目根目录下的 src
目录下,经过 Webpack
构建后生成的生产环境
代码放在 dist
目录下
现在,在 src
目录下创建 JS 执行入口文件 index.js
,在 dist
目录下创建页面入口文件 index.html
dist
目录下的页面入口文件 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>learn webpack</title>
</head>
<body>
<div id="app"></div>
<!--导入 Webpack 输出的 JavaScript 文件-->
<script src="./bundle.js"></script>
</body>
</html>
src
目录下的 JS 执行入口文件 index.js
function show(content) {
window.document.getElementById('app').innerText = 'Hello,' + content;
}
show('Webpack');
webpack.config.js
基础配置
const path = require('path');
module.exports = {
// 入口文件的配置项
entry: {
index: './src/index.js'
},
// 出口文件的配置项
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
// 模块:例如转换 es6, less, 压缩, 图片转换等...
module: {},
// 插件,用于生产模板和各项功能
plugins: [],
// 配置 webpack 开发本地服务器功能
devServer: {}
}
此时项目目录结构如下:
|-- webpack-demo
|-- dist
| |__ index.html
|-- node_modules
|-- src
| |__ index.js
|__ package.json
|__ webpack.config.js
Webpack
构建命令之前已经在 package.json
文件中定义了 start
任务
"scripts": {
"start": "webpack --config webpack.config.js"
}
此时在终端中执行 npm run start
命令来执行相应的 Webpack
构建命令,你会发现在 dist
目录下生成了 bundle.js
文件
在浏览器中打开 dist
目录中的 index.html
,就会显示 Hello,Webpack
dist
目录下的 index.html
能根据指定的 html 模板
动态生成index.html
能动态加载 Webpack
打包后生成的多个 js 文件index.html
进行压缩(如:去除注释、空格等)Webpack
打包后生成的文件名要不一样,防止浏览器缓存旧的文件前三个需求可以通过使用 Webpack
插件 html-webpack-plugin
实现
html-webpack-plugin GitHub 地址
html-webpack-plugin 插件用法
深入浅出webpack教程系列4-插件使用之html-webpack-plugin配置(上)
html-webpack-plugin
内部集成了 html-minifier
, 这个压缩选项同 html-minifier
的压缩选项相同
参考:html-minifier GitHub 地址
|-- webpack-demo
|-- node_modules
|-- src
| |-- js
| | |__ index.js
| |
| |__ index.html
|__ package.json
|__ webpack.config.js
src
目录下的 index.html
是 html-webpack-plugin
插件要用到的模板文件,内容如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- 获取 html-webpack-plugin 插件配置项中的 title 信息 -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="app"></div>
</body>
</html>
html-webpack-plugin
插件cnpm i -D html-webpack-plugin
webpack.config.js
中引入并配置 html-webpack-plugin
插件const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 入口文件的配置项
entry: {
index: './src/js/index.js'
},
// 出口文件的配置项
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/bundle.js'
},
// 模块:例如转换 es6, less, 图片转换等...
module: {},
// 插件,用于生产模板等各项功能
plugins: [
new HtmlWebpackPlugin({
title: 'learn webpack', // 标题
template: './src/index.html', // html 模板
filename: 'index.html', // 生成的 HTML 名称
inject: 'true', // 引入生成的 js 文件,false 则不引入
minify: {
removeComments : true, //去掉注释
collapseWhitespace : true //去掉空行
}
})
],
// 配置 webpack 开发本地服务器功能
devServer: {}
}
执行 npm run start
后,会生成 dist
目录,内容如下:
|-- webpack-demo
|-- dist
|-- js
| |__ bundle.js
|
|__ index.html
webpack.config.js
中配置出口文件名// 出口文件的配置项
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/bundle.js'
}
其中 filename
支持 4 种动态名称:id, name, hash, chunkhash
// 使用入口名字
filename: '[name].bundle.js'
// 使用内部块 id
filename: '[id].bundle.js'
// 对每次构建使用唯一的 hash 值,所有输出的文件都是用的这个 hash 值
// 只要有任何一个文件内容改变了,hash 值就会变
filename: '[name].[hash].bundle.js'
// 该 hash 值是基于文件自身的内容改变的,每个文件的 hash 值根据自己的内容变化
filename: '[name].[chunkhash].bundle.js'
参考资料:
webpack 关于 Output 的文档
Webpack中hash与chunkhash的区别,以及js与css的hash指纹解耦方案
现在我们将 webpack.config.js
中出口文件的配置项改为
// 出口文件的配置项
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name]-[chunkhash:8].js'
}
[chunkhash:8]
表示生成 8 位 hash 值
dist
文件夹以上需求都解决,但是当改变了 index.js
文件的内容时,生成了新的带 chunkhash
的文件,原先生成的旧文件并没有被删除,于是在打包前,需要清理掉 dist
文件夹,使用 clean-webpack-plugin
插件即可
参考资料
Webpack 官方文档
clean-webpack-plugin GitHub 地址
安装 clean-webpack-plugin
插件
cnpm i -D clean-webpack-plugin
在 webpack.config.js
中引入并配置 clean-webpack-plugin
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
// ...省略其他配置
plugins: [
new CleanWebpackPlugin(['dist']) //构建前,删除 dist 目录
]
}
clean-webpack-plugin
的一些配置选项
// 应该被清除的目录或文件
let pathsToClean = [
'dist', // 删除 'dist' 目录
'build/*.*', // 删除 'build' 目录下的所有文件
'web/*.js' // 删除 'web' 目录下所有的 js 文件
]
// 相关选项
let cleanOptions = {
// 设置绝对路径,默认为项目根目录
root: __dirname,
// 是否在控制台显示清除信息
verbose: true,
// Use boolean "true" to test/emulate delete. (will not remove files).
// Default: false - remove files
dry: false,
// If true, remove files on recompile.
// Default: false
watch: false,
// Instead of removing whole path recursively,
// remove all path's content with exclusion of provided immediate children.
// Good for not removing shared files from build directories.
exclude: [ 'files', 'to', 'ignore' ],
// allow the plugin to clean folders outside of the webpack root.
// Default: false - don't allow clean folder outside of the webpack root
allowExternal: false
}
new CleanWebpackPlugin(pathsToClean, cleanOptions)
详说 Block Formatting Contexts (块级格式化上下文)
块级格式化上下文 (block formatting context) 是页面上的一个独立的渲染区域,容器里面的子元素不会在布局上影响到外面的元素。
创建一个块级格式化上下文:
1、float
不为 none
2、position
的值为 absolute
、fixed
3、 display
为以下其中之一的值:inline-block
,table-cell
,table-caption
,flex
,inline-flex
4、overflow
除 visible
以外的值 (hidden
, auto
, scroll
)
在 CSS3 中,BFC 叫做 Flow Root,并增加了一些触发条件:
display 的值为 table-caption。
position 为 fixed,其实 fixed 是 absolute 的一个子类,因此在 CSS2.1 中使用这个值也会触发 BFC,只是在 CSS3 中更加明确了这一点。
2.1 阻止垂直方向上的外边距叠加
垂直外边距叠加规则
:
仅当两个块级元素相邻并且在同一个BFC上下文时,它们在垂直方向之间的外边距才会叠加(实际外边距以最大的那个外边距为准)。
<style type="text/css">
* {
margin: 0;
padding: 0;
}
.l-wrap {
margin: 0 auto;
width: 800px;
background: green;
}
p {
margin: 10px 0;
background: yellow;
}
</style>
<div class="l-wrap">
<p>some text 1</p>
<p>some text 2</p>
<p>some text 3</p>
</div>
以上三个 p 元素中,中间的 p 元素与相邻的 p 元素在垂直外边距上产生了叠加,因此相邻边距仍为 10px,而不会是 20px。
阻止垂直外边距叠加
:
当两个块在不同的 BFC 上下文时,垂直外边距不会叠加。
修改上面的例子,使中间的 p 元素属于新的 BFC 上下文,这时垂直相邻边距变为 10px + 10px = 20px
<style type="text/css">
* {
margin: 0;
padding: 0;
}
.l-wrap {
margin: 0 auto;
width: 800px;
background: green;
}
.l-bfc {
overflow: hidden;
}
p {
margin: 10px 0;
background: yellow;
}
</style>
<div class="l-wrap">
<p>some text 1</p>
<div class="l-bfc">
<!-- 如果直接设置 p 产生 BFC,
设置 inline-block、table-caption、inline-flex 有效,其他无效-->
<p>some text 2</p>
</div>
<p>some text 3</p>
</div>
2.2 包含浮动元素
清除浮动时,有一种方法是设置浮动元素
的父元素
overflow 属性为 hidden 或 auto 。
这正是利用了 overflow: hidden/auto 触发浮动元素的父元素的 BFC 特性,来包含浮动元素。
<style type="text/css">
* {
margin: 0;
padding: 0;
}
.l-wrap {
margin: 0 auto;
width: 800px;
background: green;
}
.l-bfc {
overflow: hidden;
}
.o-col {
float: left;
width: 31.33%;
margin: 0 1%;
background: blue;
}
</style>
<div class="l-wrap l-bfc">
<div class="o-col">col 1</div>
<div class="o-col">col 2</div>
<div class="o-col">col 3</div>
</div>
2.3 阻止元素被浮动元素覆盖
例子:
<style type="text/css">
* {
margin: 0;
padding: 0;
}
.l-wrap {
margin: 0 auto;
width: 800px;
background: green;
}
.l-bfc {
overflow: hidden;
}
.floated {
float: left;
height: 80px;
background: red;
color: white;
opacity: .6;
}
p {
background: black;
color: white;
}
</style>
<div class="l-wrap l-bfc">
<div class="floated">Floated div</div>
<p class="l-bfc" style="width: 800px">
Quae hic ut ab perferendis sit quod architecto,dolor debitis quam rem provident aspernatur tempora expedita.
</p>
</div>
可以看到类似如下,浮动元素覆盖了 p 元素上:
在图中整个黑色区域为 p 元素,p 元素的 line boxes
(文本行)进行了移位。此处 line boxes
的水平收缩为浮动元素提供了空间。
随着文字的增加,因为 line boxes
不再需要移位,文字最终将会环绕在浮动元素的下方。
给 p 元素创建一个新的 BFC,那么 p 就不会被浮动元素覆盖
<div class="l-wrap l-bfc">
<div class="floated">Floated div</div>
<p class="l-bfc" style="width: 800px">
Quae hic ut ab perferendis sit quod architecto,dolor debitis quam rem provident aspernatur tempora expedita.
</p>
</div>
如果父元素宽度不足于容纳 浮动元素和 p 元素,p元素会自动下沉到下一行显示。
<div class="l-wrap l-bfc">
<div class="floated">Floated div</div>
<p class="l-bfc" style="width: 800px">
Quae hic ut ab perferendis sit quod architecto,dolor debitis quam rem provident aspernatur tempora expedita.
</p>
</div>
gulp
的基本安装和使用参考: Gulp 中文网入门指南
1、gulp-less: 编译 less
2、gulp-ejs: 编译 ejs
3、gulp-data: 获取数据
4、gulp-rename: 文件重命名
5、gulp-clean-css: 压缩 css
6、gulp-connect: 热更新
7、gulp-plumber: 错误处理
1.1 安装 gulp-less
$ cnpm install gulp-less --save-dev
1.2 编写 task
var gulp = require('gulp');
var less = require('gulp-less');
// less 处理
gulp.task('less', function() {
return gulp.src('src/less/*.less')
.pipe(less())
.pipe(gulp.dest('dist/css'));
});
1.3 运行 task
$ gulp less
从 JSON 文件中获取数据 + ejs 模版 => 编译为 html
2.1 安装 gulp-ejs
、gulp-data
$ cnpm install gulp-ejs gulp-data --save-dev
2.2 编写 task
var gulp = require('gulp');
var ejs = require('gulp-ejs');
var data = require('gulp-data');
var path = require('path');
var fs = require('fs');
// getJsonData
var getJsonData = function(file) {
// G:\workspace\glup-test\src\index.ejs
// => src/temple/index.json
var jsonFile = 'src/temple/' + path.basename(file.path, '.ejs') + '.json';
var data = JSON.parse(fs.readFileSync(jsonFile));
return data;
}
// ejs 处理
gulp.task('ejs', function() {
return gulp.src('src/*.ejs')
.pipe(data(getJsonData))
.pipe(ejs({},{},{ext: '.html'}))
.pipe(gulp.dest('./dist'));
});
2.3 运行 task
$ gulp ejs
gulp.watch
会监听文件的修改,一旦有变化就会自动运行相应的任务。
3.1 设置监听任务
var gulp = require('gulp');
var less = require('gulp-less');
var ejs = require('gulp-ejs');
var data = require('gulp-data');
var path = require('path');
var fs = require('fs');
var srcPath = {
less: 'src/less/*.less',
ejs: 'src/*.ejs',
json: 'src/temple/*.json'
};
var buildPath = {
css: 'dist/css',
html: 'dist'
};
// 默认执行的任务
gulp.task('default', ['less', 'ejs', 'watch'], function() {
console.log('default task ok!');
})
// less 处理
gulp.task('less', function() {
return gulp.src(srcPath.less)
.pipe(less())
.pipe(gulp.dest(buildPath.css))
.pipe(connect.reload());
});
// getJsonData
var getJsonData = function(file) {
// G:\workspace\glup-test\src\index.ejs
// => src/temple/index.json
var jsonFile = 'src/temple/' + path.basename(file.path, '.ejs') + '.json';
var data = JSON.parse(fs.readFileSync(jsonFile));
return data;
}
// ejs 处理
gulp.task('ejs', function() {
return gulp.src(srcPath.ejs)
.pipe(data(getJsonData))
.pipe(ejs({},{},{ext: '.html'}))
.pipe(gulp.dest(buildPath.html))
.pipe(connect.reload());
});
// 监听任务
gulp.task('watch', function() {
console.log('watch...')
gulp.watch(srcPath.less, ['less']); // 监听 less 文件
gulp.watch(srcPath.ejs, ['ejs']); // 监听 ejs 文件
gulp.watch(srcPath.json, ['ejs']); // 监听 json 文件
})
3.2 执行监听
$ gulp watch
// 或者(默认执行 default)
$ gulp
4.1 安装 gulp-connect
$ cnpm install gulp-connect --save-dev
4.2 注册热加载任务
// 热加载
gulp.task('webserver', function() {
connect.server({
root: ['dist'], // 监控的目录
port: 5000, // 端口
livereload: true // 启用热加载
});
});
4.3 文件改动时通知 livereload
刷新页面
// less 处理
gulp.task('less', function() {
return gulp.src('src/less/*.less')
.pipe(less())
.pipe(gulp.dest('dist/css'))
.pipe(connect.reload()); // css 文件更新后通知刷新页面
});
// ejs 处理
gulp.task('ejs', function() {
return gulp.src('src/*.ejs')
.pipe(data(getJsonData))
.pipe(ejs({},{},{ext: '.html'}))
.pipe(gulp.dest('./dist'))
.pipe(connect.reload()); // html 文件更新后通知刷新页面
});
4.4 结合文件监听实现 文件改动实时刷新
/ 默认执行的任务
gulp.task('default', ['webserver', 'less', 'ejs', 'watch'], function() {
console.log('default task ok!');
})
$ gulp // 默认执行 default 任务
现在在浏览器中打开地址 locahost:5000,即可打开 dist 目录下的 index.html,当改动 less 或 ejs 文件并保存时,浏览器会自动刷新页面。
5.1 安装 gulp-plumber
$ cnpm install gulp-plumber --save-dev
gulp-plumber
可以输出 gulp
插件发生的错误,并且进程不会退出。
5.2 使用
var gulp = require('gulp');
var less = require('gulp-less');
var plumber = require('gulp-plumber');
gulp.task('less', function() {
return gulp.src('less/*.less')
.pipe(plumber()) // 在插件前使用
.pipe(less())
.pipe(gulp.dest('./dist/css'))
});
var gulp = require('gulp');
var less = require('gulp-less');
var ejs = require('gulp-ejs');
var data = require('gulp-data');
var plumber = require('gulp-plumber');
var connect = require('gulp-connect');
var path = require('path');
var fs = require('fs');
var srcPath = {
less: 'src/less/*.less',
ejs: 'src/*.ejs',
json: 'src/temple/*.json'
};
var buildPath = {
css: 'dist/css',
html: 'dist'
};
// 默认执行的任务
gulp.task('default', ['webserver', 'less', 'ejs', 'watch'], function() {
console.log('default task ok!');
})
// 热加载
gulp.task('webserver', function() {
connect.server({
root: ['dist'], // 监控的目录
port: 5000,
livereload: true
});
});
// less 处理
gulp.task('less', function() {
return gulp.src(srcPath.less)
.pipe(plumber())
.pipe(less())
.pipe(gulp.dest(buildPath.css))
.pipe(connect.reload());
});
// getJsonData
var getJsonData = function(file) {
// G:\workspace\glup-test\src\index.ejs
// => src/temple/index.json
var jsonFile = 'src/temple/' + path.basename(file.path, '.ejs') + '.json';
var data = JSON.parse(fs.readFileSync(jsonFile));
return data;
}
// ejs 处理
gulp.task('ejs', function() {
return gulp.src(srcPath.ejs)
.pipe(plumber())
.pipe(data(getJsonData))
.pipe(ejs({},{},{ext: '.html'}))
.pipe(gulp.dest(buildPath.html))
.pipe(connect.reload());
});
// 监听任务
gulp.task('watch', function() {
console.log('watch...')
gulp.watch(srcPath.less, ['less']); // 监听 less 文件
gulp.watch(srcPath.ejs, ['ejs']); // 监听 ejs 文件
gulp.watch(srcPath.json, ['ejs']); // 监听 json 文件
})
Webpack
学习第七步Less
, Sass
, Stylus
解析Less
文件参考资料:
安装 less
和 less-loader
// 简写 cnpm i -D less
cnpm install --save-dev less
// 其中 less-loader 依赖 less 服务
cnpm i -D less-loader
配置 loader
:
大概过程是:先用 less-loader
对 Less
文件进行处理生成 CSS
,再用 css-loader
对生成的 CSS
进行处理(如压缩),最后用 extract-text-webpack-plugin
提取 CSS
到单独的文件。
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// ... 省略其他配置项
module: {
rules:[
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
minimize: true
}
},{
loader: 'less-loader'
}
],
// publicPath 的设置需根据提取的 css 存放的目录层级进行改变
publicPath: '../'
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 js 文件中提取出来的 css 文件的名称,可设置路径
filename: 'css/[name]-[contenthash:8].css'
})
]
}
Sass
文件参考资料:
sass-loader GitHub 地址
sass 官方文档
安装 node-sass
和 sass-loader
cnpm i -D node-sass
// sass-loader 依赖 node-sass
cnpm i -D sass-loader
配置 loader
,并使用 extract-text-webpack-plugin
将最后生成的 CSS
文件提取到指定目录下
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// ... 省略其他配置项
module: {
rules:[
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
minimize: true
}
},{
loader: 'sass-loader'
}
],
// publicPath 的设置需根据提取的 css 存放的目录层级进行改变
publicPath: '../'
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 js 文件中提取出来的 css 文件的名称,可设置路径
filename: 'css/[name]-[contenthash:8].css'
})
]
}
Stylus
文件参考资料:
stylus-loader GitHub 地址
stylus 官方文档
安装 stylus
和 stylus-loader
cnpm i -D stylus
// stylus-loader 依赖 stylus
cnpm i -D stylus-loader
配置 loader
,并使用 extract-text-webpack-plugin
将最后生成的 CSS
文件提取到指定目录下
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// ... 省略其他配置项
module: {
rules:[
{
test: /\.styl$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
minimize: true
}
},{
loader: 'stylus-loader'
}
],
// publicPath 的设置需根据提取的 css 存放的目录层级进行改变
publicPath: '../'
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 js 文件中提取出来的 css 文件的名称,可设置路径
filename: 'css/[name]-[contenthash:8].css'
})
]
}
CSS
前缀参考资料:
postcss-loader GitHub 地址
postcss GitHub 地址
autoprefixer GitHub 地址
PostCSS
通过 JS
插件来扩展它的功能,配合插件使用可以检查 CSS
、支持 CSS Variables
和 Mixins
等很多功能。如搭配 autoprefixer
插件来给 CSS
自动添加浏览器前缀
postcss-loader
和 autoprefixer
// postcss-loader 已经集成了 postcss ,不需要额外再安装 postcss
cnpm i -D postcss-loader autoprefixer
PostCSS
推荐在项目根目录下,建立一个 postcss.config.js
文件,配置如下
module.exports = {
plugins: [
require('autoprefixer')
]
}
autoprefixer
内部使用了 Browserslist
来定义你要兼容市场上哪些浏览器;
Browserslist
定义浏览器的写法参考:Browserslist GitHub 的介绍,浏览器数据来源 Can I Use
官方推荐把配置文件写入项目根目录下的 .browserslistrc
文件或在 package.json
的 browserslist
项中配置,这里我们在 package.json
中进行配置
{
// ... 忽略其他配置项
"browserslist": [
"> 1%", // 兼容市场上浏览器份额大于 1% 的浏览器
"last 2 versions", // 兼容浏览器最新的前两个版本
"not ie <= 8" // 不兼容 IE8 及以下版本
]
}
Webpack
中配置 postcss-loader
以处理 CSS
文件为例:先用 postcss-loader
进行处理,再用 css-loader
进行处理
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// ... 省略其他配置项
module: {
rules: [
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ExtractTextPlugin.extract({
// 转换 css 文件需要使用的 Loader
use: [
{
loader: 'css-loader',
options: {
// 在 css-loader 之前有多少个 loader 应该对 @import 资源进行解析
importLoaders: 1,
minimize: true
}
},
{
loader: 'postcss-loader'
}
],
// publicPath 的设置需根据提取的 css 存放的目录层级进行改变
publicPath: '../'
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 js 文件中提取出来的 css 文件的名称,前面可加存放路径
filename: `css/[name]-[contenthash:8].css`,
})
]
}
处理 Less
, Sass
, Stylus
文件也是一样:
先用对应 Loader
处理后,再用 postcss-loader
处理,最后用 css-loader
处理
以处理 Less
文件为例
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// ... 省略其他配置项
module: {
rules:[
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: {
// 在 css-loader 之前有多少个 loader 应该对 @import 资源进行解析
importLoaders: 2,
minimize: true
}
},
{
loader: 'postcss-loader'
},
{
loader: 'less-loader'
}
],
// publicPath 的设置需根据提取的 css 存放的目录层级进行改变
publicPath: '../'
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 js 文件中提取出来的 css 文件的名称,可设置路径
filename: 'css/[name]-[contenthash:8].css'
})
]
}
CSS
当项目中使用到 CSS
框架(如:Bootstrap
)时,很多 CSS
代码是用不到的,这就需要消除未使用的 CSS
需要使用 Webpack
的 purifycss-webpack
插件,其依赖 purify-css
来移除未使用的 CSS
,它必须和 extract-text-webpack-plugin
插件一起使用
参考资料:
purifycss-webpack GitHub 地址
purify-css GitHub 地址
purifycss-webpack
和 purify-css
cnpm i -D purifycss-webpack purify-css
purifycss-webpack
purifycss-webpack
必须要配合 extract-text-webpack-plugin
来使用
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const PurifyCssPlugin = require('purifycss-webpack');
const glob = require('glob'); // 用来同步检查 html 模板
module.exports = {
// ... 省略其他配置项
module: {
rules: [
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ExtractTextPlugin.extract({
// 转换 .css 文件需要使用的 Loader
use: [
{
loader: 'css-loader',
options: {
minimize: true
}
}
],
// publicPath 的设置需根据提取的 css 存放的目录层级进行改变
publicPath: '../'
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 js 文件中提取出来的 css 文件的名称,可设置路径
filename: 'css/[name]-[contenthash:8].css'
}),
// 确保 purifycss-plugin 在 extract-text-webpack-plugin 后面
new PurifyCssPlugin({
// 必须写绝对路径,同步检查 html 模板
paths: glob.sync(path.resolve(__dirname, 'src/*.html'))
})
]
}
Webpack
学习第八步ES6
babel-loader
转换 ES6
ECMAScript 6.0
是 2015 年发布的下一代 JavaScript
语言标准,它引入了新的语法和 API
来提升开发效率。
目前部分浏览器已经支持 ES6
,但并不全面。所以在开发过程中,需要把采用 ES6
语法编写的 JS
代码转换为 ES5
语法,在 Webpack
中使用 babel-loader
来对 JS
进行转换。
参考资料:
babel-loader GitHub 地址
Babel GitHub 地址
Babel 官方文档
安装 babel-loader
和 babel-core
// babel-loader 依赖 babel-core
// babel-core 是 Babel 核心
cnpm i -D babel-core babel-loader
Babel
配置babel-loader
通过调用 Babel
完成转换工作,我们还需对 Babel
进行配置,告诉它如何转换 JS
语法,Babel
的配置文件在项目根目录下的 .babelrc
中,格式如下:
{
"plugins": [
[
"transform-runtime",
{
"polyfill": false
}
]
],
"presets": [
[
"env",
{
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}
],
"stage-2"
]
}
其中:
1、plugins
属性告诉 Babel
要使用哪些插件,插件可以控制如何转换代码
transform-runtime
对应的插件为 babel-plugin-transform-runtime
,其作用是减少冗余代码,配合 babel-runtime
一起使用。
2、presets
属性告诉 Babel
要对哪些语法特性进行转换,并用对应插件进行转换
env
对应的插件为 babel-preset-env
,根据你支持的环境自动决定使用适合的 Babel
插件(可转换当前所有 ECMAScript
标准里的最新特性)
参考:env 文档
stage-2
对应的插件为 babel-preset-stage-2
,所有使用 stage 2
(或更高)阶段的代码必备的插件 (stage 2
阶段: 是指已经被起草的特性规范,将会被纳入到标准里)
参考:stage-2 文档
Babel 配置文件中的执行顺序为:
先执行 `plugins` 再执行 `presets`
其中 `plugins` 配置顺序执行,`presets` 配置逆序执行
cnpm i -D babel-plugin-transform-runtime
cnpm i -S babel-runtime
cnpm i -D babel-preset-env
cnpm i -D babel-preset-stage-2
babel-laoder
module.exports = {
// ... 省略其他配置项
// 模块:例如转换 es6, less, 图片转换等...
module: {
rules:[
// ...省略其他配置项
{
test: /\.js$/,
use: [{
loader: 'babel-loader'
}]
}
]
}
在阅读该开发规范前需熟悉 Git 的使用,推荐如下链接:
1、Pro Git 简体中文版 (很好的学习 Git 的书)
2、git-recipes (GitHub 上开源的手册,图文并茂)
三篇关于 Git 工作流的参考文章:
1、Git 工作流程
2、常见工作流比较 (这个是上面 git-recipes
中的文章)
3、简介我的 Git Work Flow
规范不是金科玉律,实际情况可能更加复杂,大家可以根据实际情况调整规范
1.1 master 分支
(主分支):存储官方发布历史
1.2 develop 分支
(开发分支):用来整合各功能分支
1.3 feature 分支
(特性/功能分支):每个新功能都放置在自己的分支中,可以在备份/协作时推送到**仓库
feature 分支
将 develop 分支
作为父分支,当一个功能完成时,它将被合并回 develop 分支
。(功能永远不应该直接在 master 分支
上交互 )
1.4 release 分支
(发布分支):一旦 develop 分支
的新功能足够发布(或者预先确定的发布日期即将到来),从 develop 分支
fork 的 一个分支。
release 分支
的创建开始了下个发布周期,只有和发布相关的任务应该在这个分支进行,如修复 bug、生成文档等。
一旦准备好发布,发布分支将合并进 master 分支
,并打上版本号的标签。另外,它也应该合并回 develop 分支
。
发布分支命名规范:release-* or release/*
1.5 bugfix 分支
:修复在 release 分支
上发现的 bug(类似 hotfix 分支
)
标准的 GitFlow 是没有该分支的,因为可以直接在 release 分支
上提交 commit 来修复 bug
为了修复 bug 的方便,我加了这个分支规范,可以根据自己的开发规范来决定是否需要
1.6 hotfix 分支
(维护/补丁分支):用来快速给产品的发布打上补丁
这是唯一可以从 master 分支
上 fork 的分支,一旦修复完成了,它应该被并入 master
和 develop
分支,master 分支
应该打上更新的版本号的标签。
有一个专门的 bug 修复开发线使得你的团队能够处理 issues,而不打断其他工作流或是要等到下一个发布周期。你可以将维护分支看作在 master 分支
上工作的临时发布分支。
创建本地仓库后,一般还需要进行一些设置,来与远程仓库进行交互
有两种方式:本地新建仓库
或者 克隆远程仓库
1、新建并初始化本地仓库
// 创建项目目录,如 Development-spec
$ mkdir Development-spec
// 进入项目目录
$ cd Development-spec
// 初始化目录,把目录变成 Git 可以管理的仓库
// 这时当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的
$ git init
git init
命令默认创建了本地 master 分支
,所以几乎每一个仓库都有 master 分支
注意:
该 master 分支
类似于新建的孤儿分支,没有任何提交信息
使用 git branch -v
查看分支时,显示不出来
一旦有提交历史,就成为真正的分支,可以查看到
创建孤儿分支命令:git checkout --orphan <branch>
孤儿分支意思为该分支中没有任何内容,与之前创建的其他分支没有任何关联
之后所有 git 命令
都是在本地仓库目录 Development-spec
下执行
2、关联远程仓库
git remote add <name> <url>
name: 作为远程仓库的别名(一般命名为 origin)
url: 远程仓库地址,支持 HTTPS 和 SSH 协议
HTTPS 是允许匿名、只读访问仓库的简易方式
如果希望对仓库进行读写,你需要使用 SSH 协议,而且需要在托管的服务器上有一个有效的 SSH 账户
$ git remote add origin [email protected]:shushu2013/Development-specification.git
一些操作远程仓库的命令
$ git remote // 列出本地仓库和其他远程仓库的连接
$ git remote -v // 与 git remote 相同,但同时显示每个连接的 URL
$ git remote show <name> // 查看远程仓库信息,name: 为远程仓库的别名
$ git remote rm <name> // 移除名为 name 的远程仓库的连接
$ git remote rename <old-name> <new-name> // 将远程仓库别名从 old-name 重命名为 new-name
3、从远程仓库抓取数据
$ git fetch
此命令会从所有远程仓库中拉取本地仓库中没有的数据
你就可以在本地访问所有远程仓库中的所有分支,分支以远程仓库名开头
例如:远程仓库 origin 中的 master 分支,fetch 后在本地对应的分支名字是 origin/master
你可以将其中某个分支合并到本地,或者切换到这个分支,看看有些什么有趣的更新
git fetch
详细命令
$ git fetch <remote>
此命令会到远程仓库 remote 中拉取所有你本地仓库中还没有的数据
运行完成后,你就可以在本地访问该远程仓库中的所有分支,将其中某个分支合并到本地,或者只是取出某个分支,一探究竟
$ git fetch <remote> <branch>
和上一个命令相同,但是指定了远程仓库的分支名
4、跟踪远程分支
跟踪远程分支
:将本地分支和某个远程分支进行关联
关联后,该本地分支也称为 跟踪分支(tracking branch)
在该分支中输入 git push
,Git会自行推断应该向哪个服务器的哪个分支推送数据
同样,在该分支里运行 git pull
会获取远程分支索引,并把它的数据都合并到本地分支中来
设定跟踪分支相关命令
1、当分支不存在,或者是新建的孤儿分支,且没有任何提交历史,可使用如下命令,否则会报错
$ git checkout -b <branch> <remote-branch>
branch: 本地分支
remote-branch: 远程分支
该命令会创建 branch 分支,设置 branch 分支跟踪远程分支 remote-branch,并切换到 branch 分支
$ git checkout --track/-t <remote-branch>
--track/-t : -t 是 --track 命令的简写
remote-branch : 本地 fetch 下来的远程分支
该命令是 git checkout -b <branch> <remote-branch> 的简化版本,默认创建的本地分支为 remote-branch 的 branch
如:创建本地的 master 分支设定为跟踪远程仓库 origin 的 master 分支,并切换到 master 分支
$ git checkout -t origin/master
2、当分支存在,并且已有提交历史,可使用如下命令
$ git branch --set-upstream-to=<remote-branch> <branch>
该命令设置本地 branch 分支跟踪远程分支 remote-branch
注意:设置后,因为分支已经有提交历史,可能需要运行 git pull 或 git merger 命令来合并远程仓库的数据
在 2.9.0 之后,会报错:fatal: refusing to merge unrelated histories (拒绝合并历史无关的分支)
需要添加 --allow-unrelated-histories 选项
$ git merge <remote-branch> <branch> --allow-unrelated-histories
这样会产生一个新的合并提交历史
$ git clone <url> // 下载一个项目和它的整个代码历史
$ git clone <url> <directory> // 指定下载目录
// 会在当前目录下创建一个 Development-specification 目录,包含了对远程仓库的克隆
$ git clone https://github.com/shushu2013/Development-specification.git
克隆远程仓库的好处:
1、本地仓库默认关联了远程仓库,远程仓库别名为 origin
2、远程仓库的所有分支都下载到了本地,可通过 git branch -r
查看
2、默认创建了 master 分支
并设置为 origin/master
远程分支的跟踪分支
develop 分支
// 以 master 分支创建一个互补的 develop 分支
$ git branch develop master
// 把 develop 分支推送到服务器上
$ git push -u <remote> develop
develop 分支
将会包含项目中所有的历史,而 master 分支
将包含不完全的版本
其他开发者应该将**仓库克隆到本地,创建一个分支来追踪 develop 分支
:
$ git clone [email protected]:shushu2013/Development-specification.git
$ git checkout -b develop origin/develop
feature 分支
feature 分支
都应该基于 develop 分支
,而不是 master 分支
$ git checkout -b <some-feature> develop
开发者使用 [ 编辑、缓存、提交 ] 的一般约定来向功能分支 feature 分支
添加提交:
$ git status // 查看当前文件状态
$ git add <file> // 跟踪新文件、暂存已修改文件
$ git add . // . 表示所有文件
$ git commit -m "<commit-message>" // 提交更新
在添加了一些提交之后,开发者确信开发的功能已经准备好了
如果团队使用 Pull Request
,现在正是发起 Pull Request
的好时候,请求将 feature 分支
并入 develop 分支
否则,可以向下面一样,将它并入本地的 develop 分支
,然后推送到**仓库:
// 一般在远程仓库的 develop 分支会有频繁提交,需在本地更新,并解决合并冲突
$ git pull <remote> develop // 更新本地的 develop 分支数据
$ git checkout <some-feature> // 切换到 some-feature 分支
$ git rebase develop // 把整个 some-feature 分支移动到 develop 分支的后面
在合并之前,在 some-feature 分支使用 rebase 命令:
它会把整个 some-feature 分支移动到 develop 分支的后面,有效地把所有 develop 分支上新的提交并入过来
但是,rebase 为原分支上每一个提交创建一个新的提交,重写了项目历史,并且不会带来合并提交
参考:5.1-代码合并:Merge、Rebase-的选择
$ git checkout develop // 切换到 develop 分支
$ git merge --no-ff <some-feature> // 将功能分支数据合并到 develop 分支,--no-ff : 表示不使用默认的快速合并
$ git push // 推送 develop 分支数据到远程仓库的 develop 分支
---
$ git branch -d <some-feature> // 删除功能分支
release 分支
当功能开发完成或者开发周期结束,准备项目的官方发布时,新建一个 release 分支
来封装发布的准备工作
这也正是发布的版本号创建的一步:
// 从 develop 分支 fork 一个 release 分支
$ git checkout -b <release-version> develop
// 把 release 分支推送到服务器上,当正式发布到 master 分支后,可以删除本地和服务器上的 release 分支
$ git push -u <remote> <release-version>
这个 release 分支
用来整理提交,充分测试,更新文档,为即将到来的发布做各种准备
它就像是一个专门用来完善发布的功能分支,期间添加的 commit
基本都是 bug fix
开发结束后同时合并进 develop 分支
和 master 分支
,并在 master 分支
打上发布 tag
最后可以删除本地和服务器上的 release 分支
bugfix 分支
release 分支
的 bug 修复
可以直接提交 commit 修复 bug,或者在本地开一个 bugfix 分支
(类似 hotfix 分支
),修复好后合并进 release 分支
,然后删除 bugfix 分支
$ git pull <remote> <release-version> // 从远程仓库更新 release-version 分支数据
$ git checkout -b <bugfix-issue> <release-version> // fork 一个 bugfix-issue 分支,进行 bug 修复
--- bug 修复后
// 以下三步在远程仓库的 release-version 分支有频繁提交的情况下才需要
$ git pull <remote> <release-version> // 更新 release-version 分支
$ git checkout <bugfix-issue> // 切换到 bugfix-issue 分支
$ git rebase <release-version> // 把整个 bugfix-issue 分支移动到 release-version 分支的后面
$ git checkout <release-version> // 切换到 release-version 分支
$ git merge --no-ff <bugfix-issue> // 把 bugfix-issue 分支的数据合并进 release-version 分支
$ git push // 推送 release-version 分支到远程仓库
$ git branch -d <bugfix-issue> // 删除 bugfix-issue 分支
最后 release 分支
合并进 master 分支
和 develop 分支
$ git pull <remote> <release-version> // 更新 release-version 分支
$ git checkout master // 切换到 master 分支
$ git merge --no-ff <release-version> // 将 release-version 分支合并进 master 分支
$ git tag <tag-version> // 打上对应的版本标签
$ git push // 推送到远程仓库,默认不会把标签推送上去
$ git push <remote> <tag-version> // 把 tag-version 标签推送到远程仓库
-----
$ git pull <remote> develop // 更新 develop 分支
$ git checkout <release-version> // 切换到 release-version 分支
$ git rebase develop // 把整个 release-version 分支移动到 develop 分支的后面
$ git checkout develop // 切换到 develop 分支
$ git merge --no-ff <release-version> // 合并进 develop 分支,保证将来的版本也会包含 release-version 上的 bug 修复
$ git push // 推送到远程仓库
---- 正式发布完 master 分支
$ git branch -d <release-version> // 删除本地 release-version 分支
$ git push <remote> :<release-version> // 删除远程 release-version 分支
标签相关额外命令介绍,更多参考 [ Git 基础 - 打标签 ]
// 列出所有标签
$ git tag
// 打上含附注的标签
$ git tag -a <tag-version> -m <tag-message>
-m :选项则指定了对应的标签说明,Git会将此说明一同保存在标签对象中
如果没有给出该选项,Git会启动文本编辑软件供你输入标签说明
// 一次推送所有本地新增的标签上去
$ git push origin --tags
hotfix 分支
hotfix 分支
创建时间在项目正式发布之后
发布后,用户发现了 bug,这时从 master 分支
fork 一个 hotfix 分支
,修复完成后,合并回 master 分支
和 develop 分支
,最后删除这个 hotfix 分支
// 从 master 分支 fork 一个 hotfix-issue 分支,并切换到 hotfix-issue 分支
$ git checkout -b <hotfix-issue> <master>
--- 修复 bug
// 开发者使用 [ 编辑、缓存、提交 ] 的一般约定来向修复 hotfix-issue 分支 添加提交:
$ git checkout <hotfix-issue> // 切换到 hotfix-issue 分支
$ git status // 查看当前文件状态
$ git add <file> // 跟踪新文件、暂存已修改文件
// 或者使用 git add . 命令,其中 . 表示所有文件
$ git commit -m "<commit-message>" // 提交更新
--- 修复完成,合并回 master 分支 和 develop 分支
$ git checkout master
$ git merge --no-ff <hotfix-issue>
$ git tag <tag-version> // 打上标签
$ git push
$ git push <remote> <tag-version>
$ git checkout develop
$ git pull
$ git merge --no-ff <hotfix-issue>
$ git push
--- 删除 hotfix-issue 分支
$ git branch -d <hotfix-issue>
域名
:用于识别网站
例如:baidu.com、github.com 这个是一个一级域名
二级域名
:是在一级域名之下的域名,比如 domain.baidu.com 就是一个二级域名,
我们常见的 www.baidu.com也是一个二级域名,不过由于大家通常都把 www 当作是通用域名,
因此 www.baidu.com 一般被看作是一级域名。
子域名
:xxx + 域名,例如:map.baidu.com、support.github.com
子域名无需申请,当你购买了你自己的域名后,你就可以在你的域名前加上任何开头形成子域名。
例如:map.baidu.com 是 baidu.com 的子域名
只要协议、域名、端口有任何一个不同,都被当作是不同的域
https://www.baidu.com vs http://www.baidu.com 协议不同
https://www.baidu.com vs https://map.baidu.com 域名不同
https://www.baidu.com:8080 vs https://www.baidu.com:5000 端口不同
1、如果是协议和端口造成的跨域问题“前台”是无能为力的
2、在跨域问题上,域仅仅是通过“URL的首部”来识别
("URL的首部":window.location.protocol + window.location.host)
jsonp
跨域1.1 原理:
通过动态加载 JS 来实现,这种方式允许用户传递一个 callback
参数给服务端,然后服务端返回数据时会将这个 callback
参数作为函数名来包裹住 JSON
数据。
1.2 实现:
客户端
// 回调函数
function doSomething(data) {
console.log('The author is: ' + data.name);
}
// 动态创建 JS
var script = document.createElement('script');
//与服务器端约定 callback 参数,用来指定回调函数的名字
script.src = 'http://www.wenbo.com/author?callback=doSomething';
document.body.appendChild(script);
服务器端
// 服务器收到 http://www.wenbo.com/author?callback=doSomething 请求后
// 会将数据放到回调函数的参数位置返回
// doSomething({"name": "wenbo"});
// 假如服务器是 NodeJS,核心代码如下
const data = {name: 'wenbo'}
// 获取查询参数callback,就是我们在客户端定义的 doSomething
const callback = url.parse(req.url, true).query.callback
if (callback) {
const str = callback + '(' + JSON.stringify(data) + ')'
// 以字符串的形式返回
res.end(str)
} else {
res.end('hello world')
}
1.3 利用 jQuery 的 $.getJSON 进行跨域请求
该方法会自动判断是否跨域,不跨域就调用普通的 ajax 方法;跨域的话,则会以异步加载 js 文件的形式来调用 jsonp 的回调函数。
// jquery 会自动生成一个全局函数来替换 callback=? 中的 ?, 之后获取到数据又会自动销毁,实际上就是起一个临时代理函数的作用。
$.getJSON('http://www.wenbo.com/author?callback=?', function(data) {
console.log('The author is: ' + data.name);
})
1.4 优缺点
兼容性更好,在老浏览器中都可以运行。
只支持 GET 请求而不支持 POST 等其它类型的 HTTP 请求;不能解决不同域的两个页面之间如何进行 JavaScript 调用问题。
CORS
跨域CORS
( Cross-Origin Resource Sharing ) 跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何通信。
实现 CORS
通信的关键是服务器,只要服务器实现了 CORS
接口,就可以跨源通信。
2.1 基本**:
使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。
2.2 通信
服务器端:
服务器端对于 CORS
的支持,主要就是通过设置 Access-Control-Allow-Origin 来进行的。
客户端:
正常的 Ajax 请求,浏览器一旦发现 Ajax 请求跨源,就会自动添加一些附加的信息,有时还会多出一次附加的请求,但用户不会有感觉。
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.wenbo.com/author",true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
console.log(xhr.responseText);
} else {
console.log('Request status: ' + xhr.status);
}
}
}
xhr.send();
2.3 优点
CORS
支持所有类型的 HTTP 请求,开发者可以使用普通的 XMLHttpRequest 发起请求和获取数据,比 JSONP
有更好的错误处理。
WebSocket
是一种 HTML5 的一种新的协议 (IE10+),它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案,详细参考 MDN
/* websocket协议为ws/wss, 类似http/https的区别 */
wsUrl = 'wss://127.0.0.1:8090/ws/';
/* 发送 */
ws = new WebSocket(wsUrl);
/* 连接成功建立时调用 */
ws.onopen = function (event) {
console.log("websocket command onopen");
var msg = {
username: 'YeaseonZhang'
}
/* 通过 send() 方法向服务端发送消息,参数必须为字符串 */
ws.send(JSON.stringify(msg));
};
/* 服务端向客户端发送消息时调用 */
ws.onmessage = function (event) {
/* event.data包含了服务端发送过来的消息 */
console.log("websocket command onmessage: " + event.data);
if (event.data === 'success') {
/* 通过 close() 方法断开websocket连接 */
ws.close();
}
};
/* 连接被关闭时调用 */
ws.onclose = function (event) {
console.log("websocket command onclose: " + event.data);
};
/* 出现错误时调用 */
ws.onerror = function (event) {
console.log("websocket command onerror: " + event.data);
};
4.1 例子
有一个页面地址是 http://www.a.com/a.html
, 在这个页面里有一个 iframe, 它的
src 是 http://support.a.com/a.html
, 很显然它们的域是不同的,所以我们无法在http://www.a.com/a.html
页面中通过 js 来获取 iframe 中的东西.
这个时候我们只需要把 http://www.a.com/a.html
和 http://support.a.com/a.html
这两个页面的 document.domain
都设成相同的域名就可以了。注意:document.domain
只能设置为自身或更高一级的父域,且主域必须相同。
在页面 http://www.a.com/a.html
中设置 document.domain
<iframe id = "iframe" src="http://support.a.com/a.html" onload = "test()"></iframe>
<script type="text/javascript">
var username = 'wenbo';
document.domain = 'a.com'; //设置成主域
function test(){
alert(document.getElementById('iframe').contentWindow);
//contentWindow 可取得子窗口的 window 对象
}
</script>
在页面 http://support.a.com/a.html
中设置 document.domain
<script type="text/javascript">
//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
document.domain = 'a.com';
console.log(window.parent.username) // 访问父窗口成员
</script>
5.1 原理
父窗口可以对 iframe 进行 URL 读写,iframe 也可以读写父窗口的 URL,URL 有一部分被称为 hash(#号及其后面的字符),此方法的原理就是改变 URL 的 hash 部分来进行双向通信。(由于 IE、Chrome 在不同域下不允许修改 parent.location.hash 的值,所以要借助于父窗口域名下的一个代理 iframe 来通信)
5.2 例子
<iframe id="iframe-b" src="http://www.b.com/b.html"></iframe>
<script>
var bPage = document.getElementById('iframe-b');
/* 修改iframe 下的 hash */
bPage.src = bPage.src + '#user=yeaseonzhang';
function cb (res) {
console.log(res);
}
</script>
<iframe id="iframe-c" src="http://www.a.com/c.html"></iframe>
<script>
var cPage = document.getElementById('iframe-c');
/* 修改iframe 下的 hash */
cPage.src = cPage.src + '#age=20';
window.onhashchange = function () {
/* 监听 hash 变化,改变 c 页面的 hash */
cPage.src = cPage.src + location.hash;
// doSomething...
}
</script>
<script>
window.onhashchange = function () {
/* 监听 hash 变化,调用 a 页面的函数 */
window.parent.parent.cb('success: ' + location.hash.substring(1));
}
</script>
由于 a页面和 c页面是同域资源,所以 c页面可以通过 window.parent.parent 访问 a页面资源。
5.3 优缺点
改变 hash 会产生浏览器历史记录,有些浏览器不支持(IE8+ 支持) onhaschange 事件,需要轮询来获知 URL 的改变。数据直接暴露了 URL 中,且数据容量和类型有限。
6.1 原理
window 对象有个 name 属性,该属性有个特征:在一个窗口的生命周期内,窗口载入的所有的页面都是工享一个 window.name 的,每个页面对 window.name 都有读写的权限,且 window.name 并不会因新页面的载入而进行重置。
类似 location.hash
,需要借助第三个同域页面辅助
6.2 例子
<script>
var iframe = document.createElement('iframe');
/* step 1 加载跨域页面 */
iframe.src = 'http://www.b.com/b.html';
var domain = 'diff';
/* 监听iframe加载 */
iframe.onload = function () {
if ('diff' == domain) {
/* step 2 重定向到同域页面 */
iframe.contentWindow.location = 'http://www.a.com/c.html';
domain = 'same';
} else if ('same' == domain) {
/* 获取同域资源的window.name信息 */
cb(iframe.contentWindow.name);
/* 清空数据 */
iframe.contentWindow.name = '';
}
}
function cb (res) {
console.log(JSON.parse(res));
}
</script>
<scirpt>
/* 写入相关数据 */
var obj = {
username: 'wenbo'
}
window.name = JSON.stringify(obj);
</script>
同域 c页面,可以是一个空页面,不需要进行任何操作。
7.1 原理
利用 HTML5 的 postMessage 方法跨域(IE8+支持),该方法包括接受信息的"message"事件和发送信息的"postMessage"方法。
参考:MDN
window.postMessage(message, targetOrigin, [transfer])
window: 指目标窗口 (window.frames属性的成员或由window.open方法创建的窗口),也就是给哪个window发送消息。
message: 消息内容
targetOrigin: 接受消息窗口的源,即 "协议 + 域名 + 端口"。也可以设置为通配符 *,向所有窗口发送
transfer: 可选参数(布尔值),是一串和 message 同时传递的 `Transferable` 对象。这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
发送者和接收者都可以通过监听 message
事件来接收信息。
message
事件的事件对象 event
包含三个属性:
event.source: 发送消息的窗口对象的引用,可以用此在两个窗口建立双向通信
event.origin: 发送消息的 URL
event.data: 消息内容
7.2 例子
<script>
var newWindow = window.open('http://www.b.com/b.html');
/* 向b.html发送消息 */
newWindow.postMessage('Hello', 'http://www.b.com/b.html');
/* 双向通信,接收b.html的回复消息 */
var onmessage = function (event) {
var data = event.data;
var origin = event.origin;
var source = event.source;
if (origin == 'http://www.b.com/b.html') {
console.log(data); //Nice to see you!
}
};
// 监听 message 消息
window.addEventListener('message', onmessage, false);
</scirpt>
<script>
var onmessage = function (event) {
var data = event.data;
var origin = event.origin;
var source = event.source;
if (origin == 'http://www.a.com/a.html') {
console.log(data); //Hello
/* 回复a.html的消息 */
source.postMessage('Nice to see you!', 'http://www.a.com/a.html');
}
};
// 监听 message 消息
window.addEventListener('message', onmessage, false);
</script>
或者
<script type="text/javascript">
var ifr = document.createElement('iframe');
ifr.src = 'https://www.b.com/b.html';
document.body.appendChild(ifr);
window.onload = function() {
// 若写成'http://www.b.com/c/proxy.html'效果一样
// 若写成'http://www.c.com'就不会执行postMessage了
var targetOrigin = 'https://www.b.com';
ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>
<script type="text/javascript">
window.addEventListener('message', function(event){
// 通过origin属性判断消息来源地址
if (event.origin == 'http://www.a.com') {
alert(event.data); // 弹出"I was there!"
// 对a.com、index.html中window对象的引用
// 但由于同源策略,这里event.source不可以访问window对象
alert(event.source);
}
}, false);
</script>
Webpack
学习第五步经过前两天的学习总结,我们把配置文件分为 开发环境配置
和 生产环境配置
,因为 开发环境配置
相对简单,所有在之后的学习中我们统一修改的是 生产环境配置
文件。
在使用各种 Webpack
的插件和 Loader
时,一定要注意使用的版本号,因为不同版本有些配置项可能不同。
那如何找到对应版本的文档呢?
方法一:
去项目的 GitHub
仓库地址,可以选择代码的 Branche
中的 Tags
标签来选择对应的代码版本,一般都有对应的使用文档。
方法二:
如果方法一不起作用,那可以去项目的官方网站,结合其中的文档和更新日志来查看对应版本有哪些改变的地方。
需求:
图片可能在 CSS
, JS
, HTML
中使用,如下所示
// 在 css 中使用图片
.bg {
background-image: url('../image/index/001.jpg');
}
// 在 js 中使用图片
function addImg() {
var imgUrl = require('../image/index/001.jpg');
var img = document.createElement('img');
img.src = imgUrl;
document.body.appendChild(img);
}
// 在 html 中使用图片
<img src="image/index/001.jpg" >
我们需要把这些图片提取出来,并且放到指定目录下。
file-loader
:解析提取 CSS
, JS
中的图片
url-loader
:类似 file-loader
,可设置把图片文件转为 Base64 的格式写入 CSS
、JS
html-withimg-loader
:解析提取 HTML
中的 img
标签引用的图片
参考资料:
file-loader GitHub 地址
url-loader GitHub 地址
html-withimg-loader GitHub 地址
file-loader
安装:
cnpm i -D file-loader
配置:
module.exports = {
// ... 省略其他配置项
// 模块:例如转换 es6, less, 图片转换等...
module: {
rules:[
// ...省略其他配置项
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]-[hash:8].[ext]',
// 提取出来的图片放置在 image 目录下,后面的 / 不能省略
outputPath: 'image/'
}
}
]
}
]
}
url-loader
安装:
cnpm i -D url-loader
配置:
module.exports = {
// ... 省略其他配置项
// 模块:例如转换 es6, less, 图片转换等...
module: {
rules:[
// ...省略其他配置项
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 5120,
name: '[name]-[hash:8].[ext]',
outputPath: 'image/'
}
}
]
}
]
}
}
注意:url-loader
封装了 file-loader
,使用时并不需要再安装 file-loader
limit
参数(字节/Byte):
当图片文件小于设定的值时,会把文件转换为 DataURL
(Base64 格式)
当图片文件大于设定的值时,会默认调用 file-loader
进行处理,参数也会直接传给 file-loader
,可通过 fallback
参数指定其他 loader
来处理
html-withimg-loader
html-withimg-loader
处理 HTML
文件中 <img>
标签引用的图片
安装:
cnpm i -D html-withimg-loader
配置:
module.exports = {
// ... 省略其他配置项
// 模块:例如转换 es6, less, 图片转换等...
module: {
rules:[
// ...省略其他配置项
{
test: /\.(htm|html)$/i,
use: ['html-withimg-loader']
}
]
}
}
CSS
中图片路径不对当用 extract-text-webpack-plugin
插件提取 CSS
并指定存放目录时,生成的 CSS
中图片的引用路径并不正确
(注意:如果不指定 CSS
存放的目录,图片引用路径是正确的)
例如 extract-text-webpack-plugin
及图片处理配置如下:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// 入口文件的配置项
entry: {
index: './src/js/index.js'
},
// 出口文件的配置项
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name]-[chunkhash:8].js'
},
// ... 省略其他配置项
module: {
rules: [
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ExtractTextPlugin.extract({
// 使用 css-loader 对 css 进行压缩
use: [
{
loader: 'css-loader',
options: {
minimize: true
}
}
]
})
},
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]-[hash:8].[ext]',
// 提取出来的图片放置在 image 目录下,后面的 / 不能省略
outputPath: 'image/'
}
}
]
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 js 文件中提取出来的 css 文件的名称,前面可加存放路径
filename: `css/[name]-[contenthash:8].css`,
})
]
}
原始 main.css
内容及目录结构
.bg {
background-image: url('../image/index/001.jpg');
}
|-- src
|-- css
| |__ main.css
|
|-- image
|--index
|__ 001.jpg
打包构建后生成的 index-5c5d3df7.css
内容及目录结构
.bg {
background-image: url('image/001-de229472.jpg');
}
|-- dist
|-- css
| |__ index-5c5d3df7.css
|
|-- image
|__ 001-de229472.jpg
显然在 index-5c5d3df7.css
文件中是引用不到 001-de229472.jpg
如果我们不指定提取后 CSS
文件的存放目录,则默认生成在项目打包构建后的输出目录下
|-- dist
|
|-- image
| |__ 001-de229472.jpg
|
|__ index-5c5d3df7.css
<!-- index-5c5d3df7.css 内容不变-->
.bg {
background-image: url('image/001-de229472.jpg');
}
这时 index-5c5d3df7.css
中对图片的引用地址是正确的
配置 ExtractTextPlugin.extract()
中的 publicPath
选项,该选项要根据
new ExtractTextPlugin()
中是否对 CSS
设置了存放目录来配置
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// ... 省略其他配置项
module: {
rules: [
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ExtractTextPlugin.extract({
// 使用 css-loader 对 css 进行压缩
use: [
{
loader: 'css-loader',
options: {
minimize: true
}
}
],
publicPath: '../'
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 js 文件中提取出来的 css 文件的名称,前面可加存放路径
filename: `css/[name]-[contenthash:8].css`,
})
]
}
为什么 publicPath
要用设置为 ../
,因为 webpack
生成的 css
,默认位于项目打包构建后的输出目录下,这时图片的引用地址没有问题。
但是我们在 plugin
配置项 new ExtractTextPlugin({filename: 'css/[name]_[contenthash:8].css'})
中,设置了生成的 CSS
文件存放在 css
目录下,相当于比原先的位置更深了一层,所以需要修正 CSS
文件中的引用路径往上再查找一层。
如果设置生成的 CSS
目录为 css/index/[name]_[contenthash:8].css
,则 publicPath 要设置为 ../../
,相当于需要往上再查找两层。
Webpack
学习第六步目标:
JS
、CSS
、HTML
CSS
和 HTML
的压缩之前在我们的配置中,我们已经对 CSS
和 HTML
进行了压缩
使用了 css-loader
和 html-webpack-plugin
插件,配置如下
module.exports = {
// ... 省略其他配置项
// 模块:例如转换 es6, less, 图片转换等...
module: {
rules:[
// ...省略其他配置项
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ExtractTextPlugin.extract({ // 从 js 文件中提取 css
// 使用 css-loader 对 css 进行压缩
use: [
{
loader: 'css-loader',
options: {
minimize: true
}
}
]
})
}
]
}
plugins: [
// ...省略其他配置项
new HtmlWebpackPlugin({
// ...省略其他配置项
// 对生成的 HTML 进行压缩配置
minify: {
removeComments : true, //去掉注释
collapseWhitespace : true //去掉空行
}
})
]
}
其中 css-loader
内部使用的是 cssnano
来对 css
进行压缩,所以 cssnano
支持的压缩选项都可以在 css-loader
中进行配置,一般不进行配置,使用默认配置。
参考资料:
注意:
这里使用的 css-loader 版本是 v0.28.8,其内部使用的 cssnano 版本是 v3.10.0,
目前 cssnano 版本已经升级到了 v4.0.0,官方文档也是 v4.0.0 了,
如果要查看 v3.10.0 的信息应该对比 cssnano 的更新日志来看。
JS
使用 Webpack
内置的 uglifyjs-webpack-plugin
插件来压缩 JS
,配置如下
参考资料(注意插件的版本号):
uglifyjs-webpack-plugin GitHub 地址
UglifyJS2 配置选项
const uglifyjs = require('uglifyjs-webpack-plugin');
module.exports = {
// ... 省略其他配置项
plugins: [
// ...省略其他配置项
new uglifyjs({
// 相关配置参数与使用的 uglifyjs-webpack-plugin 版本有关
// 生成 sourceMap,配合 devtool: 'sourceMap' 使用
sourceMap: true,
compress: {
// 去掉 console 打印信息
drop_console: true
},
beautify: false // 格式化输出
})
]
}
注意:
我这里使用的 Webpack 3.6.0 中的 uglifyjs-webpack-plugin 版本是 v0.4.6,
其中 uglifyjs-webpack-plugin 插件使用的是 UglifyJS2 版本是 v2.8.29
配置 uglifyjs-webpack-plugin 参数时要查看对应版本的官方文档
(一般去官方 GitHub 仓库查看对应的版本即可)
deb
包在 electron-ssr releases 下载 electron-ssr_0.2.3_amd64.deb
安装包
$ sudo dpkg -i electron-ssr_0.2.3_amd64.deb
SwitchyOmega_Chromium
去 GitHub 官方仓库的 release 地址 下载插件
安装后,再设置 SwitchyOmega
为系统代理
最后,设置一下 SwitchyOmega
代理服务器
代理服务器地址: 127.0.0.1 -- 本地启动的 electron-ssr 使用的地址
代理端口: 1080 -- electron-ssr 本地监听的端口
以上参数可在 electron-ssr 中配置,并且 SwitchyOmega 参数要做对应的更改
参考资料:
在linux环境安装shadowsocksR客户端
wget https://github.com/the0demiurge/CharlesScripts/blob/master/charles/bin/ssr
ssr 是一个 shell 脚本
$ sudo mv ssr /usr/local/bin
$ sudo chmod 766 /usr/local/bin/ssr
$ ssr install
$ ssr config
相关说明
# 作者:老徐
# SSR免费分享网站(所有帐号均来源于网上别人的分享):http://ss.pythonic.life
# 源代码主页:https://github.com/the0demiurge
# 访问https://github.com/the0demiurge/CharlesScripts/blob/master/charles/bin/ssr获取本脚本的最新版
# 使用方法:把该脚本放到$PATH里面并加入可执行权限就行(比如说放到/usr/local/bin)
# 首次使用输入ssr install后安装时会自动安装到 $HOME/.local/share/shadowsocksr
# 输入ssr config进行配置,输入JSON格式的配置文件
# 输入ssr uninstall卸载
# 输入ssr help 展示帮助信息
sudo vi /etc/rc.local
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
sudo ssr start
exit 0
配置文件为 config.json
{
"server": "jp02.fss.fun",
"server_ipv6": "::",
"server_port": 12853,
"local_address": "127.0.0.1",
"local_port": 1080,
"password": "12064277",
"group": "Charles Xu",
"method": "aes-256-cfb",
"protocol": "auth_aes128_md5",
"protocol_param": "",
"obfs": "tls1.2_ticket_auth_compatible",
"obfs_param": "",
"speed_limit_per_con": 0,
"speed_limit_per_user": 0,
"additional_ports" : {}, // only works under multi-user mode
"additional_ports_only" : false, // only works under multi-user mode
"timeout": 120,
"udp_timeout": 60,
"dns_ipv6": false,
"connect_verbose_info": 0,
"redirect": "",
"fast_open": false
}
配置信息参考:
Configuration via Config File
ssr 免费订阅
electron-ssr
源码并编译$ git clone https://github.com/erguotou520/electron-ssr
# or npm install or cnpm install
$ yarn
# 打包构建
$ npm run build
构建好后,在 build
目录会生成 deb
的包,安装即可
Webpack
学习第三步需要用到 css-loader
和 style-loader
加载器
参考资料:
css-loader GitHub 地址
style-loader GitHub 地址
安装 css-loader
和 style-loader
cnpm i -D style-loader css-loader
配置 webpack.config.js
中的 module
项中的 rules
项
module.exports = {
// ... 省略其他配置项
// 模块:例如转换 es6, less, 图片转换等...
module: {
rules:[
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ['style-loader', 'css-loader?minimize']
}
]
}
}
如上配置告诉 Webpack
在遇到以 .css
结尾的文件时先使用 css-loader
读取 CSS
文件,再交给 style-loader
把 CSS
内容注入到 JavaScript
里。 在配置 Loader
时需要注意的是:
use
属性的值需要是一个由 Loader
名称组成的数组,Loader
的执行顺序是由后到前的;Loader
都可以通过 URL querystring
的方式传入参数,例如 css-loader?minimize
中的 minimize
告诉 css-loader
要开启 CSS 压缩
。style-loader
的工作原理大概是把 CSS
内容用 JavaScript
里的字符串存储起来, 在网页执行 JavaScript
时通过 DOM
操作动态地往 HTML head
标签里插入 HTML style
标签。
在上面重新执行构建后,你会发现 index.js
文件被更新了,里面注入了在 index.css
中的 CSS
,而不是额外生成一个 CSS 文件
,这里需要用到 Webpack
的插件,即 extract-text-webpack-plugin
参考资料:
extract-text-webpack-plugin GitHub 地址
安装 extract-text-webpack-plugin
插件
cnpm i -D extract-text-webpack-plugin
webpack
相关配置
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// ... 省略其他配置项
module: {
rules: [
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ExtractTextPlugin.extract({
// 转换 .css 文件需要使用的 Loader
use: ['css-loader?minimize']
})
}
]
},
plugins: [
new ExtractTextPlugin({
// 从 js 文件中提取出来的 css 文件的名称,前面可加存放路径
filename: `css/[name]_[contenthash:8].css`,
})
]
}
Webpack
第四步1、提供 HTTP 服务
而不是使用本地文件预览;
2、监听文件的变化并自动刷新网页,做到实时预览;
3、支持 Source Map
,以方便调试;
参考资料
webpack-dev-server GitHub 地址
DevServer 官方文档
webpack-dev-server
的安装及使用安装
cnpm i -D webpack-dev-server
首先在 package.json
中配置 npm script
脚本命令来启动 webpack-dev-server
"scripts": {
"dev": "webpack-dev-server"
}
然后在 Webpack
配置文件的 devServer
配置项中对 webpack-dev-server
进行配置
const webpack = require('webpack');
module.exports = {
// ... 省略其他配置项
plugins: [
new webpack.HotModuleReplacementPlugin() // 开启模块热替换需要使用的插件
],
// 配置 webpack 开发本地服务器功能
devServer: {
// 服务器的IP地址,默认为 localhost
host:'localhost',
// 服务端压缩是否开启
compress:true,
// 配置服务端口号,默认为 8080
port:8000,
// 使用默认浏览器自动打开网页
open: true,
// 开启模块热替换 (不刷新网页进行更新)
hot: true
}
}
webpack-dev-server
会把 Webpack
构建出的文件保存在内存中,在要访问输出的文件时,必须通过 HTTP 服务访问
这里开启模块热替换有两种方法:
方法一:通过 'webpack-dev-server' 命令行选项 '--hot' 来指定开启模块热替换
// 在 package.json 文件中配置 npm script 脚本命令
"scripts": {
"dev": "webpack-dev-server --hot"
}
方法二:在配置文件的 'devServer' 选项中开启模块热替换时,需要在 'plugins' 配置项中加入
'Webpack' 自带 'HotModuleReplacementPlugin' 插件,所以需要先引入 `webpack`,代码如下:
const webpack = require('webpack');
module.exports = {
// ... 省略其他配置项
plugins: [
new webpack.HotModuleReplacementPlugin()
],
// 配置 webpack 开发本地服务器功能
devServer: {
// 开启模块热替换 (不刷新网页进行更新)
hot: true
// ... 省略其他配置项
}
}
如果使用方法二,没有引入 HotModuleReplacementPlugin
插件,网页控制台会显示出错信息: Uncaught Error: [HMR] Hot Module Replacement is disabled.
出错相关参考资料
GitHub 上相关的 issue
webpack-dev-server 官方文档相关说明
1.2.1、构建失败,报错信息为:Cannot use [chunkhash] for chunk in 'js/[name]-[chunkhash:8].js' (use [hash] instead)
这里注意,如果开启了模块热替换,Webpack
构建打包不能使用 chunkhash
来给输出的文件命名,而应该使用 hash
,否则构建会失败
module.exports = {
// 入口文件的配置项
entry: {
index: './src/js/index.js'
},
// 出口文件的配置项
output: {
path: path.resolve(__dirname, 'dist'),
// 当开启模块热替换时,使用 hash
filename: 'js/[name]-[hash:8].js'
}
// ... 省略其他配置项
}
1.2.2、修改 css
文件后,页面并没有变化
原因是,使用了 ExtractTextPlugin
插件来提取 css
到一个独立的样式文件,而 ExtractTextPlugin
不支持模块热替换,因此不要单独提取 css
module.exports = {
// ... 省略其他配置项
module: {
rules:[
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ExtractTextPlugin.extract({ // 从 js 文件中提取 css
// 转换 .css 文件需要使用的 Loader
use: ['css-loader?minimize']
})
}
]
}
}
改为
module.exports = {
// ... 省略其他配置项
module: {
rules:[
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ['style-loader', 'css-loader?minimize']
}
]
}
}
这时修改 css
文件会触发模块热替换, 因为 style-loader
会注入用于接受 CSS
的代码。
Source Map
配置参考资料
webpack 官方文档 使用 Source Map
Devtool 官方文档
有两种方式
方法一:在 Webpack
配置文件中配置 devtool
选项即可
devtool
配置可选项很多,具体可参考上面的 Devtool 官方文档
常用的有如下四个:
source-map
: 在一个单独文件中产生一个完整且功能完全的文件。这个文件具有最好的 source map
,但是它会减慢打包速度;cheap-module-source-map
: 在一个单独的文件中产生一个不带列映射的 map
,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列;eval-source-map
: 使用 eval
打包源文件模块,在同一个文件中生产干净的完整版的 source map
,但是对打包后输出的 JS 文件的执行具有性能和安全的隐患;cheap-module-eval-source-map
: 这是在打包文件时最快的生产 source map
的方法,生产的 source map
会和打包后的 JavaScript
文件同行显示,没有影射列;如果大型项目可以使用 source-map
,如果是中小型项目使用 eval-source-map
就完全可以
配置选项如下
module.exports = {
// ... 省略其他配置项
devtool: 'source-map'
}
方法二:在 webpack-dev-server
命令行选项加入 --devtool source-map
参数来指定以 source-map
的方式生成 Source Map
(--devtool
可选参数同方法一)
"scripts": {
"dev": "webpack-dev-server --devtool source-map"
}
在上面使用 webpack-dev-server
开启模块热替换,提高开发效率的同时,需要对 Webpack
配置文件做相应的修改,其中一些修改和代码优化设置不兼容,这就迫使我们做出改变,针对 开发环境
和 生产环境
,分别使用不同的配置文件
开发环境
的 Webpack
配置文件为 webpack.config.dev.js
生产环境
的 Webpack
配置文件为 webpack.config.prod.js
package.json
中的构建脚本改为
"scripts": {
"dev": "webpack-dev-server --config webpack.config.dev.js",
"build": "webpack --config webpack.config.prod.js"
}
webpack.config.dev.js
配置文件内容如下
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
// 入口文件的配置项
entry: {
index: './src/js/index.js'
},
// 出口文件的配置项
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name]-[hash:8].js'
},
// 模块:例如转换 es6, less, 图片转换等...
module: {
rules:[
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ['style-loader', 'css-loader?minimize']
}
]
},
// 插件,用于生产模板等各项功能
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
title: 'learn webpack', // 标题
template: './src/index.html', // html 模板
filename: 'index.html', // 生成的 HTML 名称
inject: 'true', // 引入生成的 js 文件,false 则不引入
minify: {
removeComments : true, //去掉注释
collapseWhitespace : true //去掉空行
}
})
],
// 配置 Source Maps
devtool: 'source-map',
// 配置 webpack 开发本地服务器功能
devServer: {
// 服务器的IP地址,默认为 localhost
host:'localhost',
// 服务端压缩是否开启
compress:true,
// 配置服务端口号,默认为 8080
port:8000,
// 使用默认浏览器自动打开网页
open: true,
// 开启模块热替换 (不刷新网页进行更新)
hot: true
}
}
webpack.config.prod.js
配置文件内容如下
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// 入口文件的配置项
entry: {
index: './src/js/index.js',
// list: './src/js/list.js'
},
// 出口文件的配置项
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name]-[chunkhash:8].js'
},
// 模块:例如转换 es6, less, 图片转换等...
module: {
rules:[
{
// 用正则去匹配要用该 loader 转换的 CSS 文件
test: /\.css$/,
use: ExtractTextPlugin.extract({ // 从 js 文件中提取 css
// 转换 .css 文件需要使用的 Loader
use: ['css-loader?minimize']
})
}
]
},
// 插件,用于生产模板等各项功能
plugins: [
new CleanWebpackPlugin(['dist']), //构建前,删除 dist 目录
new HtmlWebpackPlugin({
title: 'learn webpack', // 标题
template: './src/index.html', // html 模板
filename: 'index.html', // 生成的 HTML 名称
inject: 'true', // 引入生成的 js 文件,false 则不引入
minify: {
removeComments : true, //去掉注释
collapseWhitespace : true //去掉空行
}
}),
new ExtractTextPlugin({
// 从 .js 文件中提取出来的 .css 文件的名称
filename: 'css/[name]_[contenthash:8].css'
})
]
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.