问题
将处理程序绑定到相同元素的click和dblclick事件是不合适的。
触发的事件顺序因浏览器而异,有些在dblclick之前接收到两个点击事件,而其他事件只有一个。
双击灵敏度(双击检测到的点击之间的最大时间)可能因操作系统和浏览器而异,并且通常是用户可配置的。
所以最好不要在同一个元素下绑定click和dbclick事件。
一分耕耘一分收获
IE8下用babel转换会报错:
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
解决方法:
$ npm install --save-dev babel-plugin-transform-es2015-modules-simple-commonjs
配置:
//webpack.config.js
"plugins": ["transform-es2015-modules-simple-commonjs"]
ES6 + angular1 + webpack,遇到controller文件里的constructor运行2次?
那是因为声明了2次controller
,在配置中配了app.controller('MyController')
,然后又在页面中使用了ng-controller
,导致运行了2次,坑爹~
Babel转ES5后IE8下的兼容性解决方法。
1)webpack配置文件,增加插件transform-es3-property-literals和transform-es3-member-expression-literals
const webpackdevConfig = {
entry: entry,
output: {
path: path.join(__dirname, 'dist/js'),
filename: '[name].js',
publicPath: '/static/'
},
plugins: [
new webpack.NoErrorsPlugin(),
],
module: {
loaders: [
{
test: /\.js$/, loader: ['babel'], include: [path.join(new_dir, 'src')],
query:{
"presets": ["es2015", "stage-0"],
"plugins" : [
"transform-es3-property-literals",
"transform-es3-member-expression-literals",
]
}
},
{test: /\.scss$/, loaders: ['style', 'css', 'sass'], include: path.join(new_dir, 'src/style')},
{test: /\.(jpg|png)$/, loader: 'url-loader?limit=8192', include: path.join(new_dir, 'src/img')}
]
}
}
2)模块导出不能使用 export default ,改为export { xxx }
3)模块引入使用 import { } from ‘xxx’
4)引入es5-shim.min.js和es5-sham.min.js
Webpack is on the verge of having its latest major version released, and it’s expected to drop very soon. However, the main thing holding the release back is documentation, and the code is mostly written. I recently took the time to update our work project from Webpack 1 to 2, and thought I’d document the steps taken for anyone else who wants to make the move.
You can also check out the migration guide on the Webpack documentation.
Install the webpack2
The first thing to do is install the latest version. Because it’s not a stable release, you have to specify the exact beta version you’d like. At the time of writing it’s 2.1.0-beta.25:
npm install --save-dev webpack@2.1.0-beta.25
If you’re using any other Webpack plugins, be aware that they might need updating. For example, the Extract Text Plugin has a v2 in beta also:
npm install --save-dev extract-text-webpack-plugin@2.0.0-beta.4
module.loaders => module.rules
This is not a breaking change because module.loaders will continue to be supported, but in the future it will be deprecated in favour of module.rules. This is just an easy renaming step.
// before
modules: {
loaders: {...}
}
// after
modules: {
rules: {...}
}
resolve.modulesDirectories => resolve.modules
Another renaming step, the resolve options have been renamed:
// before
resolve: {
modulesDirectories: [...],
}
// after
resolve: {
modules: [...],
}
No webpack.optimize.OccurenceOrderPlugin
It’s now included by default, so there is no need to have this in our config.
Configuring loaders
At work we’re using postcss and postcss-loader to load our CSS through Webpack. The loader used to expect a top level postcss key in the Webpack config. As of Webpack 2 this is no longer allowed; we can instead define an options key when we configure the loader. This replaces the query option from Webpack 1. Any plugin that looked for top level configuration will have to be swapped to this style.
// before, in Webpack top level
postcss: {
plugins: ...
}
// after
module: {
rules: [{
test: /\.scss$/,
use: [
{
loader: 'postcss-loader',
options: {
plugins: ...
}
},
'sass-loader'
]
}]
}
ExtractTextPlugin changes
The above change to loader configuration also makes it way easier to configure multiple loaders; previously it would only be possible to pass an array of loaders in string form to some plugins, such as ExtractTextPlugin:
// Webpack 1
ExtractTextPlugin.extract(
'style-loader',
'css-loader!postcss-loader!sass-loader'
);
This quickly got very hard to work with if you had to pass options:
// Webpack 1
ExtractTextPlugin.extract(
'style-loader',
'css-loader?modules-true!postcss-loader!sass-loader'
);
But now Webpack 2 can deal with arrays of objects to configure loaders. We can replace the above with:
// Webpack 2
var loaders = [
{
loader: 'css-loader',
options: {
modules: true
}
},
{
loader: 'postcss-loader'
},
{
loader: 'sass-loader'
}
]
Whereas in Webpack 1 we used the key query for configuring loaders, we now use options. ExtractTextPlugin can now take this array, rather than only allowing the string form:
// Webpack 2
ExtractTextPlugin.extract({
fallbackLoader: 'style-loader',
loader: loaders,
})
Stop Babel from compiling ES2015 modules
Webpack 1 wasn’t able to parse ES2015 modules, so Babel would convert them into CommonJS. Webpack 2 can parse ES2015 modules, and is able to eliminate dead code based on which modules are used, so it’s recommended that you tell Babel not to convert modules into CommonJS. You can do this by changing your .babelrc:
// before
"presets": ["es2015"]
// after
"presets": [
["es2015", { "modules": false }]
]
We’ve seen a good file size saving by doing this, and hopefully this will continue to improve in the future!
Fin
Webpack 2 offers better performance, improved bundling and a much nicer experience when configuring it. Given that the code is so stable, despite its beta status, I highly recommend giving it a go on your projects when you can.
引用jQuery插件时会报”jQuery is not defined”,解决方法:
(1). Prefer unminified CommonJS/AMD over dist
Most modules link the dist version in the main field of their package.json. While this is useful for most developers, for webpack it is better to alias the src version because this way webpack is able to optimize dependencies better (e.g. when using the DedupePlugin).
// webpack.config.js
module.exports = {
...
resolve: {
alias: {
jquery: "jquery/src/jquery"
}
}
};
However, in most cases the dist version works just fine as well.
(2). Use the ProvidePlugin to inject implicit globals
Most legacy modules rely on the presence of specific globals, like jQuery plugins do on $ or jQuery. In this scenario you can configure webpack, to prepend var $ = require(“jquery”) everytime it encounters the global $ identifier.
var webpack = require("webpack");
...
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
]
(3). Use the imports-loader to configure this
Some legacy modules rely on this being the window object. This becomes a problem when the module is executed in a CommonJS context where this equals module.exports. In this case you can override this with the imports-loader.
Run npm i imports-loader --save-dev
and then
module: {
loaders: [
{
test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
loader: "imports?this=>window"
}
]
}
The imports-loader can also be used to manually inject variables of all kinds. But most of the time the ProvidePlugin is more useful when it comes to implicit globals.
(4). Use the imports-loader to disable AMD
There are modules that support different module styles, like AMD, CommonJS and legacy. However, most of the time they first check for define and then use some quirky code to export properties. In these cases, it could help to force the CommonJS path by setting define = false.
module: {
loaders: [
{
test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/,
loader: "imports?define=>false"
}
]
}
(5). Use the script-loader to globally import scripts
If you don’t care about global variables and just want legacy scripts to work, you can also use the script-loader. It executes the module in a global context, just as if you had included them via the <script>
tag.
(6). Use noParse to include large dists
When there is no AMD/CommonJS version of the module and you want to include the dist, you can flag this module as noParse. Then webpack will just include the module without parsing it, which can be used to improve the build time. This means that any feature requiring the AST, like the ProvidePlugin, will not work.
module: {
noParse: [
/[\/\\]node_modules[\/\\]angular[\/\\]angular\.js$/
]
}
常见的loader
{
test: /\.js/,
loader: "babel-loader",
query: {
"presets": ["es2015", 'stage-0'],
plugins: []
},
exclude: /(node_modules)/
},
{
test: /\.css$/,
//注意:此处不能有autoprefix-loader
loader: ExtractText.extract('style-loader', 'css-loader')
},
{
test: /\.(png|gif|jpg|jpeg)$/,
loader: "url?name=img/[hash:8].[ext]"
},
{
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?name=font/[name].[ext]&limit=10000&minetype=application/font-woff'
},
{
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?name=font/[name].[ext]&limit=10&minetype=application/font-woff'
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?name=font/[name].[ext]&limit=10&minetype=application/octet-stream'
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file'
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url?limit=10&minetype=image/svg+xml'
}
样式的loader
(1)style-loader|css-loader is the way to do it just with css
(2)style-loader|css-loader|postcss-loader is the way to post-process css
(3)style-loader|css-loader|less-loader is the way to do it if you want to use less
(4)style-loader|css-loader|postcss-loader|less-loader is the way to post-process the compiled less (css)
ES6引用art-template
,报错:Module not found: Error: Cannot resolve module 'fs'
,解决方法:
//webpack.config.js
module.exports={
node: {
fs: "empty"
}
};
webpack开发时打包第三方库都比较大,可以通过配置alias
指向压缩版本:
resolve: {
alias: {
modernizr$: path.resolve(__dirname, "./.modernizrrc"),
bootstrap: path.join(__dirname, "./node_modules/bootstrap/dist/js/bootstrap.min.js"),
bootstrapCss: path.join(__dirname, "./node_modules/bootstrap/dist/css/bootstrap.min.css"),
fontAwesomeCss: path.join(__dirname, "./node_modules/font-awesome/css/font-awesome.min.css")
}
}
引用第三方插件如:ulynlist
,需要配置别名:
alias: {
'ulynlist.table': path.join(__dirname, './src/sslib/ulynlist/ulynlist.table.js'),
'ulynlist.pager': path.join(__dirname, './src/sslib/ulynlist/ulynlist.pager.js'),
artTemplate: path.join(__dirname, './node_modules/art-template')
}
import样式文件页面会有闪烁现象,这是可以通过extract-text-webpack-plugin
抽取样式文件,就不会有这个问题了
合并jquery和第三方插件时,外面是读取不到$
和jQuery
的,所以我们可以通过expose-loader
把jQuery
对象导出到全局:
You can either do this when you require it:
require("expose?$!jquery");
or you can do this in your config:
loaders: [
{ test: require.resolve('jquery'), loader: 'expose?jQuery!expose?$' }
]
相同的道理,如果插件里有this,则我们可以通过imports-loader
把this当成window处理:
{
test: require.resolve('respond.js'),
loader: 'imports?this=>window'
}
使用第三方插件,如果其没有判断commonjs这一层,则我们可以配合exports-loader和imports-loader使用,如eos3还有eos服务,eos3需要导出eos对象,eos服务的js需要导入eos这个对象:
import 'exports?eos!./lib/eos3/eos3';
//这里define设为false,防止组件判断为AMD模块
import 'imports?define=>false,this=>window!./lib/auth/dmService';
webpack-dev-server默认是localhost访问,不能通过ip访问,我们可以配置如下:
webpack-dev-server –host 0.0.0.0
大项目打包时经常内存溢出:
修改打包命令:
1 | node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --env=prod --progress --profile --colors |
一个loader是一个node模块,导出一个函数。这个函数当资源被转换时会执行,这个loader有一个入参:待转换资源名称。可以在loader中通过this
访问上下文。
一个同步的loader可以仅仅返回一个值。在其他情况下,loader可以通过this.callback(err, values)
返回多个值。
一个loader被期望返回一个或两个值,第一个值返回字符串或buffer类型的javascript代码,第二个返回sourceMap。
在复杂的情况下,当多个loader链接时,仅仅只要最后一个loader返回资源文件,仅仅第一个loader期望返回一个或两个值(javascript代码或buffer)。
Example:
module.exports = function(source){
return source;
};
//
// Identity loader with SourceMap support
module.exports = function(source, map) {
this.callback(null, source, map);
};
(按优先顺序排序,第一个应该得到最高优先级)
loader可以被链接,他们不应该转换为javascript代码,如果他们不需要的话。
例如:从模板中通过查询参数渲染html,我会编写一个从源代码中编译的loader,执行他并返回一个包含包含html的字符串,这是不好的。而是我应该编写装载程序在这个用例中的每一个任务,并将它们全部应用(流水线):
加载程序生成模块应尊重相同的设计原则,如正常模块。例子:这是一个糟糕的设计:(不模块化,全局状态,…)
require("any-template-language-loader!./xyz.atl");
var html = anyTemplateLanguage.render("xyz");
大多数装载机是可缓存的,所以他们应该标志本身作为缓存。只要在loader中调用cacheable
。
//Cacheable identity loader
module.exports = function(source){
this.cacheable();
return source();
};
一个加载程序应该独立于编译的其他模块(由装载程序发布的这些模块的期望)。一个程序应该独立于以前的编译的模块。
如果loader需要依赖第三方资源(如从系统中读取文件),他们必须要写清楚,此信息用于无效的缓存装载机和编译在观看模式。
// Loader adding a header
var path = require("path");
module.exports = function(source) {
this.cacheable();
var callback = this.async();
var headerPath = path.resolve("header.js");
this.addDependency(headerPath);
fs.readFile(headerPath, "utf-8", function(err, header) {
if(err) return callback(err);
callback(null, header + "\n" + source);
});
};
一些语言有自己的解决依赖图式,例如css的@import
和url(...)
。这些必须被模块系统解决。
有两个方法可以做到:
require
;this.resolve
解析路径;例子1:css-loader把依赖转换成require其他样式文件。
例子2:less-loader不转换为require,因为因为所有的less文件需要编译一次跟踪变量和混合,因此,less-loader扩展less编译器一个自定义的路径解决方法,该自定义逻辑使用this.resolve解决模块的系统配置文件(走样,自定义模块目录,等)。
If the language only accept relative urls (like css: url(file) always means ./file), there is the ~-convention to specify references to modules:
url(file) -> require("./file")
url(~module) -> require("module")
don’t generate much code that is common in every module processed by that loader. Create a (runtime) file in the loader and generate a require to that common code.
don’t put absolute paths in to the module code. They break hashing when the root for the project is moved. There is a method stringifyRequest
in loader-utils which converts an absolute path to an relative one.
Example:
var loaderUtils = require("loader-utils");
return "var runtime = require(" +
loaderUtils.stringifyRequest(this, "!" + require.resolve("module/runtime")) +
");";
using a peerDependency allows the application developer to specify the exact version in package.json if desired. The dependency should be relatively open to allow updating the library without needing to publish a new loader version.
"peerDependencies": {
"library": "^1.3.5"
}
there are situations where your loader requires programmable objects with functions which cannot stringified as query-string. The less-loader, for example, provides the possibility to specify LESS-plugins. In these cases, a loader is allowed to extend webpack’s options-object to retrieve that specific option. In order to avoid name collisions, however, it is important that the option is namespaced under the loader’s camelCased npm-name.
有两种级别的添加hash方法:
{
output: {
path: path.join(__dirname, "assets", "[hash]"),
publicPath: "assets/[hash]/",
filename: "output.[hash].bundle.js",
chunkFilename: "[id].[hash].bundle.js"
}
}
output: { chunkFilename: "[chunkhash].bundle.js" }
Note that you need to reference the entry chunk with its hash in your HTML. You may want to extract the hash or the filename from the stats.
In combination with Hot Code Replacement you must use option 1, but not on the publicPath config option.
You probably want to access the final filename of the asset to embed it into your HTML. This information is available in the webpack stats. If you are using the CLI you can run it with –json to get the stats as JSON to stdout.
You can add a plugin such as assets-webpack-plugin to your configuration which allows you to access the stats object. Here is an example how to write it into a file:
plugins: [
function() {
this.plugin("done", function(stats) {
require("fs").writeFileSync(
path.join(__dirname, "..", "stats.json"),
JSON.stringify(stats.toJson()));
});
}
]
The stats JSON contains a useful property assetsByChunkName which is a object containing chunk name as key and filename(s) as value.
Note: It’s an array if you are emitting multiple assets per chunk. I. e. a JavaScript file and a SourceMap. The first one is your JavaScript source.
为了压缩你的脚本(和你的样式,如果你用css-loader
的话),webpack支持下面两个途径:
--optimize-minimize 或者 new webpack.optimize.UglifyJsPlugin()
webpack给你的模块和块赋予了id来区别他们,webpack可以为经常用到的id通过下面途径得到最小id长度:
--optimize-occurrence-order resp. new webpack.optimize.OccurrenceOrderPlugin()
如果你使用第三方库有相同依赖时,会重复引用相同的文件,webpack可以找到并去重,默认是不开启的,可以使用一下方法开启:
--optimize-dedupe resp. new webpack.optimize.DedupePlugin()
限制快的最大大小 –optimize-max-chunks 15 new webpack.optimize.LimitChunkCountPlugin({maxChunks: 15})
限制块的最小大小 –optimize-min-chunk-size 10000 new webpack.optimize.MinChunkSizePlugin({minChunkSize: 10000})
Webpack会照顾它通过合并块(它会合并块,有重复的模块)。不会有东西合并到入口块,所以不会影响初始页面加载时间。
A Single-Page-App is the type of web app webpack is designed and optimized for.
You may have split the app into multiple chunks, which are loaded at your router. The entry chunk only contains the router and some libraries, but no content. This works great while your user is navigating through your app, but for initial page load you need 2 round trips: One for the router and one for the current content page.
If you use the HTML5 History API to reflect the current content page in the URL, your server can know which content page will be requested by the client code. To save round trips to the server you can include the content chunk in the response: This is possible by just adding it as script tag. The browser will load both chunks parallel.
<script src="entry-chunk.js" type="text/javascript" charset="utf-8"></script>
<script src="3.chunk.js" type="text/javascript" charset="utf-8"></script>
You can extract the chunk filename from the stats. (stats-webpack-plugin could be used for exports the build stats)
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
entry: {
p1: "./page1",
p2: "./page2",
p3: "./page3",
ap1: "./admin/page1",
ap2: "./admin/page2"
},
output: {
filename: "[name].js"
},
plugins: [
new CommonsChunkPlugin("admin-commons.js", ["ap1", "ap2"]),
new CommonsChunkPlugin("commons.js", ["p1", "p2", "admin-commons.js"])
]
};
// <script>s required:
// page1.html: commons.js, p1.js
// page2.html: commons.js, p2.js
// page3.html: p3.js
// admin-page1.html: commons.js, admin-commons.js, ap1.js
// admin-page2.html: commons.js, admin-commons.js, ap2.js
Advanced hint: You can run code inside the commons chunk:
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
entry: {
p1: "./page1",
p2: "./page2",
commons: "./entry-for-the-commons-chunk"
},
plugins: [
new CommonsChunkPlugin("commons", "commons.js")
]
};
See also multiple-entry-points example and advanced multiple-commons-chunks example.
{ test: /\.css$/, loader: "style-loader!css-loader" }
这种情况下会在页面添加style标签式的样式
可以使用extract-text-webpack-plugin
抽成样式文件。
var ExtractTextPlugin = require("extract-text-webpack-plugin");
...
loaders: [
// Extract css files
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
// Optionally extract less files
// or any other compile-to-css language
{
test: /\.less$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader")
}
// You could also use other loaders the same way. I. e. the autoprefixer-loader
],
plugins: [
new ExtractTextPlugin("[name].css")
]
plugins: [
new ExtractTextPlugin("style.css", {
allChunks: true
})
]
和CommonsChunkPlugin
一起使用,commons块就会生成commons.css样文件。
plugins: [
new webpack.optimize.CommonsChunkPlugin("commons", "commons.js"),
new ExtractTextPlugin("[name].css")
]