webpack对样式的处理 导入样式require和import的区别

html-css024

webpack对样式的处理 导入样式require和import的区别,第1张

可以在js中引入样式文件

require('myStyle.css')

这时我们便需要引入相应的webpack loader来帮助我们解析这段代码。

一般来说需要引入css-loader和style-loader,其中css-loader用于解析,而style-loader则将解析后的样式嵌入js代码。

// webpack配置如下

{

module: {

loaders: [

{ test: /\.$/, loader: "style-loader!css-loader" }

]

}

}

可以发现,webpack的loader的配置是从右往左的,从上面代码看的话,就是先使用css-loader之后使用style-loader。

同理,如果你使用less来写样式的话,则需要先用less-loader来编译样式文件为css文件,再继续使用css-loader与style-loader。

{

module: {

loaders: [

{ test: /\.$/, loader: "style-loader!css-loader!less-loader" }

]

}

}

我们知道,webpack配置loader时是可以不写loader的后缀明-loader,因此css-loader可以写为css。

将样式抽取出来为独立的文件

将require引入的样式嵌入js文件中,有好处也有坏处。好处是减少了请求数,坏处也很明显,就是当你的样式文件很大时,造成编译的js文件也很大。

我们可以使用插件的方式,将样式抽取成独立的文件。使用的插件就是extract-text-webpack-plugin

基本用法如下

var ExtractTextPlugin = require("extract-text-webpack-plugin")

module.exports = {

module: {

loaders: [

{ test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") }

]

},

plugins: [

new ExtractTextPlugin("styles.css")

]

}

根据插件在github上的解释,ExtractTextPlugin.extract可以有三个参数。

第一个参数是可选参数,传入一个loader,当css样式没有被抽取的时候可以使用该loader。

第二个参数则是用于编译解析的css文件loader,很明显这个是必须传入的,就像上述例子的css-loader。

第三个参数是一些额外的备选项,貌似目前只有传入publicPath,用于当前loader的路径。

那什么时候需要传入第一个参数呢,那就得明白什么时候样式不会被抽取出来。

了解过code splittiog的同学便会知道,我们有些代码在加载页面的时候不会被使用时,使用code splitting,可以实现将这部分不会使用的代码分离出去,独立成一个单独的文件,实现按需加载。

那么如果在这些分离出去的代码中如果有使用require引入样式文件,那么使用ExtractTextPlugin这部分样式代码是不会被抽取出来的。

这部分不会抽取出来的代码,可以使用loader做一些处理,这就是ExtractTextPlugin.extract第一个参数的作用。

根据上面的案例,ExtractTextPlugin需要配合plugin使用。

new ExtractTextPlugin([id: string], filename: string, [options])

该插件实例的唯一标志,一般是不会传的,其自己会生成。

文件名。可以是[name]、[id]、[contenthash]

[name]:将会和entry中的chunk的名字一致

[id]:将会和entry中的chunk的id一致

[contenthash]:根据内容生成hash值

options

allchunk: 是否将所有额外的chunk都压缩成一个文件

disable:禁止使用插件

这里的参数filename里如何理解呢?上述案例指定了一个固定的名字,因此便会生成一个styles.css文件。

那么像[name]、[id]这些如何理解。这个在你有多个entry的时候,便需要使用这种方式来命名。

var ExtractTextPlugin = require("extract-text-webpack-plugin")

module.exports = {

entry: {

"script": "./src/entry.js",

"bundle": "./src/entry2.js",

},

...

module: {

loaders: [

{ test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") }

]

},

plugins: [

new ExtractTextPlugin("[name].css")

]

}

这时候便会生成两个css文件,一个是script.css,另一个便是bundle.css。那些[id]、[contenthash]也是一个道理。

只要明白,在你有多个entry是,一定要使用这种方式来命名css文件。

最后还有那个allchunks又是什么呢?很简单,还记得前面提到的code splitting么?将该参数配置为true,那么所有分离文件的样式也会全部压缩到一个文件上。

plugins: [

new ExtractTextPlugin("[name].css", {allChunks: true})

]

postcss

以前我们写样式时,有些样式不同浏览器需要加不同的前缀,如-webkit-。现在有了构建工具,我们便不需要再去关注这些前缀了,构建工具会自动帮我们加上这些前缀。

对于webpack我们自然想到需要使用loader或者plugin来帮助我们做这些事情,查了下发现autoprefixer-loader已经废弃不再维护了,推荐使用posscss

postcss是用于在js中转换css样式的js插件,需要搭配其他插件一起使用,这点和babel6一样,本身只是个转换器,并不提供代码解析功能。

这里我们需要autoprefixer插件来为我们的样式添加前缀。首先下载该模块。

npm install autoprefixer --save-dev

接着便可以配置webpack了

var autoprefixer = require('autoprefixer')

module.exports = {

...

module: {

loaders: [

...

{

{

test: /\.css$/,

loader: ExtractTextPlugin.extract(["css-loader", "postcss-loader"])

},

}

]

},

postcss: [autoprefixer()],

...

}

查看一下抽取出来的样式文件便可以发现已经加上了前缀

a {

display: flex

}

/*compiles to:*/

a {

display: -webkit-box

display: -webkit-flex

display: -ms-flexbox

display: flex

}

另外autoprefixer还可以根据目标浏览器版本生成不同的前缀个数,例如你的应用的使用用户如果大多数是使用比较新版本的浏览器,那么便可以做如下配置。

postcss: [autoprefixer({ browsers: ['last 2 versions'] })]

这是生成的样式便会有些不一样,还是上面的例子

a {

display: flex

}

/*compiles to:*/

a {

display: -webkit-flex

display: -ms-flexbox

display: flex

}

postcss后记

这里再说一个问题,有些童鞋可能会在css文件中使用@import引入其他样式文件,但是使用autoprefixer发现,import进来的样式没有处理,如下面所示:

/*myStyle.css:*/

body {

background-color: gray

}

.flex {

display: flex

}

/*myStyle2.css:*/

@import "./myStyle.css"

.div {

color: red

}

/*autoprefixer之后*/

body {

background-color: gray

}

.flex {

display: -webkit-box

display: -webkit-flex

display: -ms-flexbox

display: flex

}

body {

background-color: gray

}

.flex {

display: flex

}

.div {

color: red

}

要解决这个问题,postcss有个解释,它让我们使用postcss-import插件,再配合autoprefixer

postcss: function(webpack) {

return [

postcssImport({

addDependencyTo: webpack

}),

autoprefixer

]

},

其实我们是不推荐使用@import的,心细的童鞋可以看到最后生成的样式文件有样式是重复的。

所以一般我们应该是在js中使用require来引入样式文件。可以参考的说法这里

样式压缩

压缩代码我们可以使用webpack的内置插件UglifyJsPlugin来做,它既可以压缩js代码也可以压缩css代码。

plugins: [

...

new webpack.optimize.UglifyJsPlugin({

compress: {

warnings: false

}

}),

...

]

其实并不能说是在压缩css代码,本质来说还是压缩js代码,再将这块代码输出到css文件中。

使用CommonsChunkPlugin抽取公共代码

首先要明确一点CommonsChunkPlugin是在有多个entry时使用的,即在有多个入口文件时,这些入口文件可能会有一些共同的代码,我们便可以将这些共同的代码抽取出来成独立的文件。明白这一点非常重要。(搞了很久才明白的一点,唉~~~~)

如果在多个entry中require了相同的css文件,我们便可以使用CommonsChunkPlugin来将这些共同的样式文件抽取出来为独立的样式文件。

module.exports = {

entry: {

"A": "./src/entry.js",

"B": "./src/entry2.js"

},

...

plugins: [

new webpack.optimize.CommonsChunkPlugin({name: "commons", filename: "commons.js"}),

...

]

}

当然,这里不止会抽取共同的css,如果有共同的js代码,也会抽取成为commons.js。

这里有个有趣的现象,抽取出来的css文件的命名将会是参数中name的值,而js文件名则会是filename的值。

CommonsChunkPlugin好像只会将所有chunk中都共有的模块抽取出来,如果存在如下的依赖

// entry1.js

var style1 = require('./style/myStyle.css')

var style2 = require('./style/style.css')

// entry2.js

require("./style/myStyle.css")

require("./style/myStyle2.css")

// entry3.js

require("./style/myStyle2.css")

使用插件后会发现,根本没有生成commons.css文件。

如果我们只需要取前两个chunk的共同代码,我们可以这么做

module.exports = {

entry: {

"A": "./src/entry.js",

"B": "./src/entry2.js",

"C": "./src/entry3.js"

},

...

plugins: [

new webpack.optimize.CommonsChunkPlugin({name: "commons", filename: "commons.js", chunks: ['A', 'B']}),

...

]

}

1. 为什么用 webpack?他像 Browserify, 但是将你的应用打包为多个文件. 如果你的单页面应用有多个页面, 那么用户只从下载对应页面的代码. 当他么访问到另一个页面, 他们不需要重新下载通用的代码.他在很多地方能替代 Grunt 跟 Gulp 因为他能够编译打包 CSS, 做 CSS 预处理, 编译 JS 方言, 打包图片, 还有其他一些.它支持 AMD 跟 CommonJS, 以及其他一些模块系统, (Angular, ES6). 如果你不知道用什么, 就用 CommonJS.2. Webpack 给 Browserify 的同学用对应地:browserify main.js >bundle.jswebpack main.js bundle.jsWebpack 比 Browserify 更强大, 你一般会用 webpack.config.js 来组织各个过程:// webpack.config.jsmodule.exports = { entry: './main.js', output: {filename: 'bundle.js' }}这仅仅是 JavaScript, 可以随意添加要运行的代码.3. 怎样启动 webpack切换到有 webpack.config.js 的目录然后运行:webpack 来执行一次开发的编译webpack -p for building once for production (minification)webpack -p 来针对发布环境编译(压缩代码)webpack --watch 来进行开发过程持续的增量编译(飞快地!)webpack -d 来生成 SourceMaps4. JavaScript 方言Webpack 对应 Browsserify transform 和 RequireJS 插件的工具称为 loader. 下边是 Webpack 加载 CoffeeScript 和 Facebook JSX-ES6 的配置(你需要 npm install jsx-loader coffee-loader):// webpack.config.jsmodule.exports = { entry: './main.js', output: {filename: 'bundle.js' }, module: {loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'jsx-loader?harmony' } // loaders 可以接受 querystring 格式的参数] }}要开启后缀名的自动补全, 你需要设置 resolve.extensions 参数指明那些文件 Webpack 是要搜索的:// webpack.config.jsmodule.exports = { entry: './main.js', output: {filename: 'bundle.js' }, module: {loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'jsx-loader?harmony' }] }, resolve: {// 现在可以写 require('file') 代替 require('file.coffee')extensions: ['', '.js', '.json', '.coffee'] }}5. 样式表和图片首先更新你的代码用 require() 加载静态资源(就像在 Node 里使用 require()):require('./bootstrap.css')require('./myapp.less')var img = document.createElement('img')img.src = require('./glyph.png')当你引用 CSS(或者 LESS 吧), Webpack 会将 CSS 内联到 JavaScript 包当中, require() 会在页面当中插入一个 `<style>标签. 当你引入图片, Webpack 在包当中插入对应图片的 URL, 这个 URL 是由require()` 返回的.你需要配置 Webpack(添加 loader):// webpack.config.jsmodule.exports = { entry: './main.js', output: {path: './build', // 图片和 JS 会到这里来publicPath: 'http://mycdn.com/', // 这个用来成成比如图片的 URLfilename: 'bundle.js' }, module: {loaders: [ { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // 用 ! 来连接多个人 loader { test: /\.css$/, loader: 'style-loader!css-loader' }, {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} // 内联 base64 URLs, 限定 <=8k 的图片, 其他的用 URL] }}6. 功能开关有些代码我们只想在开发环境使用(比如 log), 或者 dogfooging 的服务器里边(比如内部员工正在测试的功能). 在你的代码中, 引用全局变量吧:if (__DEV__) { console.warn('Extra logging')}// ...if (__PRERELEASE__) { showSecretFeature()}然后配置 Webpack 当中的对应全局变量:// webpack.config.js// definePlugin 接收字符串插入到代码当中, 所以你需要的话可以写上 JS 的字符串var definePlugin = new webpack.DefinePlugin({ __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')), __PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false'))})module.exports = { entry: './main.js', output: {filename: 'bundle.js' }, plugins: [definePlugin]}然后你在控制台里用 BUILD_DEV=1 BUILD_PRERELEASE=1 webpack 编译. 注意一下因为 webpack -p 会执行 uglify dead-code elimination, 任何这种代码都会被剔除, 所以你不用担心秘密功能泄漏.7. 多个进入点(entrypoints)比如你用 profile 页面跟 feed 页面. 当用户访问 profile, 你不想让他们下载 feed 页面的代码. 因此你创建多个包: 每个页面一个 "main module":// webpack.config.jsmodule.exports = { entry: {Profile: './profile.js',Feed: './feed.js' }, output: {path: 'build',filename: '[name].js' // 模版基于上边 entry 的 key }}针对 profile, 在页面当中插入 <script src="build/Profile.js"></script>. feed 页面也是一样.8. 优化共用代码feed 页面跟 profile 页面共用不要代码(比如 React 还有通用的样式和 component). Webpack 可以分析出来他们有多少共用模块, 然后生成一个共享的包用于代码的缓存.// webpack.config.jsvar webpack = require('webpack')var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js')module.exports = { entry: {Profile: './profile.js',Feed: './feed.js' }, output: {path: 'build',filename: '[name].js' }, plugins: [commonsPlugin]}在上一个步骤的 script 标签前面加上 <script src="build/common.js"></script>你就能得到廉价的缓存了.9. 异步加载CommonJS 是同步的, 但是 Webpack 提供了异步指定依赖的方案. 这对于客户端的路由很有用, 你想要在每个页面都有路由, 但你又不像在真的用到功能之前就下载某个功能的代码.声明你想要异步加载的那个"分界点". 比如:if (window.location.pathname === '/feed') { showLoadingState() require.ensure([], function() { // 语法奇葩, 但是有用hideLoadingState() require('./feed').show()// 函数调用后, 模块保证在同步请求下可用 })} else if (window.location.pathname === '/profile') { showLoadingState() require.ensure([], function() {hideLoadingState() require('./profile').show() })}Webpack 会完成其余的工作, 生成额外的 chunk 文件帮你加载好.Webpack 在 HTML script 标签中加载他们时会假设这些文件是怎你的根路径下. 你可以用 output.publicPath 来配置.// webpack.config.jsoutput: {path: "/home/proj/public/assets", // path 指向 Webpack 编译能的资源位置publicPath: "/assets/" // 引用你的文件时考虑使用的地址

不能。

HTML可能经过文本压缩及字符转换。

CSS可能做过自动追加前缀,压缩,或者根本就是用预编译器写出来的。

Javascript可能经过压缩,优化。

另外,如果打包配置中未设置pathinfo为true的话,将不会包含源文件路径。

就算多增加一个空格也和原始的不同了吧?

不过,当webpack打包时,可以配置生成.map文件,使用此文件可以还原原文件。

你需要问原作者索要。