您的位置:首页 > Web前端 > CSS

使用webpack4从零开始打包不使用框架的多页面应用,webpack打包多页面应用,使用postcss中px-2-vw实现响应式。

2019-06-29 11:28 1241 查看

前言,最近一直在研究webpack相关的知识,我发现webpack打包单页面应用(使用react或者vue构建的项目)会很方便,教程也很多,但是像有时候我们写的很简单的多页面应用,并且不使用框架的情况下也想使用webpack对项目进行压缩,打包和转码,但是这期间会有很多疑惑,比如怎么处理各种文件,怎么把具有html结构的代码放到dist目录,并且怎么把html文件里的img:src图片导入到目标文件夹,并且怎么把这些图片放到img文件夹中,怎么把css单独放到一个文件夹中等等,这篇文章致力于解决这类问题。完成不使用框架的情况下多页面的完整构建。

 

首先我们要安装npm,直接去node官网下载相应的版本就行了,不再赘述。然后我们创建一个webpack-demo文件夹,初始化我们的文件夹npm init -y 生成package.json文件,这里面又我们的项目信息

[code]{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
-- "main": "index.js",
++ "private":true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

删除"main":"index.js"以及添加”private:true,目的:我们自己在配置文件中定义入口文件,然后为了防止我们的文件被以为发布所以设为私有的

然后需要安装webpack相关的东西

[code]npm install webpack webpack-cli  -D

安装的webpack是核心模块不多讲,webpack-cli是你想在命令行窗口执行webpack命令就需要安装这个模块。

如果我们是全局安装进行打包的时候可以直接运行npm run webpack index.js,但是不推荐全局安装,这时候我们安装在项目文件夹下就需要使用 npx run webpack index.js。因为直接npm run webpack index.js默认是从全局作用域下去找的webpack,如果没有全局安装会报错,而npx则会从项目的node_modules目录下去找webpack运行。

我们可以配置package.json中的scripts脚本来简化命令

[code]{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
-- "main": "index.js",
++ "private":true,
"scripts": {
"build":"webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}

现在就可以直接用npm run build来进行打包,因为在scripts中配置了后默认就是从最近的node_module中执行,所以不用npx了。

我们准备一个相应的项目目录进行说明我们平时遇到的项目需求

现在我们的目的是将src文件夹下的index.html,floor.html,delect.html生成到dist目录对应的文件,并且三个文件分别独立,这个三个文件分别依赖index.scss,index.js;floor.scss,floor.js;delect.scss,delect.js。

1.webpack.config.js配置

一.配置入口出口文件

webpack.config.js

[code]const path = require('path');

module.exports = {

entry:{
index:'./src/index.js',
floor:'./src/floor.js',
delect:'./src/delect.js'
},
output:{
filename:'[name].bundle.js',
path:path.resolve(__dirname,'dist')
}

}

这里三个入口文件分别是我们要打包的三个js文件,entry对象中的key值就是区分文件的chunk,output中[name]是一个占位符,指的是前面的chunck,所以生成的js文件分别为index.bundle.js,floor.bundle.js,delect.bundle.js,如果像要给每个文件名添加hash值可以写为:filename:'[name]_hash.bundle.js'。path指的是你要把打包后的文件放到什么位置,比如dist目录。

二.配置各种loaders

我们的webpack默认是只知道怎么处理js文件的,但是我们项目中会又各种各样的文件,比如样式相关的文件:css,scss,less。css中background:url(...)引入的图片:png、jpe?g、gif。还有各种字体文件ttf,eot等等。loaders就是当你引入了非javascript文件时,webpack打包不知道怎么处理,loaders这时候告诉webpack怎么打包处理这些非js的静态资源。

1.处理样式css文件

  1. 如果只处理css文件那么我们只需要style-loader和css-loader就行了
  2. 但是我们往往会使用sass,less等工具来编写css所以这时候就需要用到sass-loader和node-sass
  3. 如果我们想要编写css3的一些样式,比如transform我们想用webpack自己添加厂商前缀则需要postcss中的autoprefixer或者启用cssnano
  4. 我们处理如果想让网站的px转为vw单位,从而实现响应式布局,则需要postcss中的px-to-viewport

综上我们来配置处理样式文件的loaders

首先安装loaders

[code]npm install style-loader css-loader sass-loader node-sass postcss cssnano-preset-advanced postcss-import postcss-url autoprefixer postcss-px-to-viewport postcss-cssnext cssnano postcss-viewport-units --save-dev

然后配置postcss.config.js

[code]module.exports={

"plugins":{

"postcss-import":{},
"postcss-import":{},
"postcss-cssnext":{},
"postcss-px-to-viewport":{
viewportWidth: 1920, // (Number) The width of the viewport.
viewportHeight: 1080, // (Number) The height of the viewport.
unitPrecision: 3, // (Number) The decimal numbers to allow the REM units to                                grow to.
viewportUnit: 'vw', // (String) Expected units.
selectorBlackList: ['.ignore', '.hairlines', '.now-time'], // (Array) The selectors to ignore and leave as px.
minPixelValue: 1, // (Number) Set the minimum pixel value to replace.
mediaQuery: false // (Boolean) Allow px to be converted in media queries.
},
"postcss-viewport-units":{},
"cssnano":{
preset:'advanced',
autoprefixer:false,
"postcss-zindex":false
}

}

}

配置webpack.config.js

[code]module.exports = {
module:{
rules:/\.scss/,
use:[
'style-loader',
{
loader:'css-loader',
options:{
importLoaders:2
}
},
'postcss-loader',
'sass-loader'
]
}

}

2.提取出css到css文件夹,并压缩css

如果我们在js中引入了css文件,并且将样式使用style-loader添加到html中的style标签中,那么需要等js加载完毕之后才会执行,这样会在页面打开的时候出现白屏时间,在开发环境没有问题,但是在生产环境这肯定不是我们需要的,我们需要css能够单独分离出来,并用link插入到head中,并行加载。这时候需要mini-css-extract-plugin。然后使用optimize-css-assets-webpack-plugin进行代码压缩。

安装:

[code]npm install mini-css-extract-plugin optimize-css-assets-webpack-plugin --save-dev

配置webpack.config.js

[code]const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports={

module:{
rules:[
{
test:/\.scss$/,
use:[
{
loader:MiniCssExtractPlugin.loader,
options:{
publicPath:'../'
}
},
{
loader:'css-loader',
options:{
importLoaders:2
}
},
'postcss-loader',
'sass-loader'
]
}
]
},
plugins:[

new MiniCssExtractPlugin({
filename:'css/[name].css',
chunkFilename:'css/[id].css'
}),

new OptimizeCssAssetsPlugin()
]
}

 

3.处理css中引入的图片

我们要处理css中引入的图片文件可以使用file-loader或者url-loader,url-loader功能跟file-loader相似都是先解析到资源,然后把目标文件拷贝到目标地址,重命名然后更新引用的地址。但是url-loader多了一个可配置项limits:1024,这个意思是如果资源小于1k则把他解析为base64格式文件放到相应位置,而不必重新去请求这个资源。我们这里使用url-loader

安装:

[code]npm install url-loader --save-dev

配置webpack.config.js

[code]module.exports = {

module:{
rules:[
test:/\.(png|jpe?g|gif)$/i,

20000
use:[{
loader:'url-loader',
options:{
name:'[name].[ext]',//filename
outputPath:'img',//there will be an img filefolder to wrap photofile
limits:1024//if file lesser than 1k,it will be base64
}
}]
]
}

}

4.处理字体文件

我们经常引入自己的字体,比如eot、woff、woff2、ttf、svg。常用的iconfont就是引入字体文件,我们也要告诉webpack如何打包处理这些文件格式。采用file-loader

安装

[code]npm install file-loader --save-dev

配置webpack.config.js

[code]module.exports={

module:{
rules:[
{
test:/\.(woff|woff2|ttf|eot)$/i
use:[
loader:'file-loader',
options:{
outputPath:'fonts'
}
]
}
]
}
}

5.处理js文件转为es5

我们不使用框架但是我们依然喜欢使用es6语法,我们直接使用的话再版本稍微低一点的浏览器中就不能正确的解析执行,需要我们使用babel-loader将es6语法编译为es5语法。

安装:

[code]npm install -D babel-loader @babel/core @babel/preset-env

配置webpack.config.js

[code]module.exports={
module:{
rules:[
{
test:/\.m?js$/,
exclude:/(node_modules|bower_components)/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env']
}
}
}
]
}
}

6.处理html中的img引入的图片资源

当我们用html写好的结构打包到dist目录后,需要将里面的img引入的图片资源也像background:url()中使用url-loader或者file-loader做相应的处理,但是html文件中的src需要使用单独的html-loader

安装:

[code]npm install html-loader --save-dev

配置html-loader

[code]module.exports = {

module:{
rules:[
{
test:/\.html$/,
use:{
loader:'html-loader',
options:{
attrs:['img:src','img:data-src','audio:src']
minimize:true
}
}
}
]
}
}

 

7.压缩js代码

我们的js代码打包完成后最好是压缩一下,可以减少不少体积,使用uglifyjs-webpack-plugin插件来压缩js代码

安装:

[code]npm install uglifyjs-webpack-plugin --save-dev

配置webpack.config.js

[code]const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
plugins:[
new UglifyJsWebpackPlugin({
exclude:/node_modules/
})
]
}

 

二.使用clean-webpack-plugin和html-webpack-plugin清理dist目录以及生成html文件到dist目录

clean-webpack-plugin可以在每次项目打包之前将dist目录清理干净

html-webpack-plugin可以生成html文件,并且把打包生成的bundle.js文件注入到html文件body标签尾部。

然而我们的html文件结构式单独写好的,比如这种

[code]<!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>Document</title>
</head>

<body>
<header>.......</header>
<main>.......</main>
<footer>.......</footer>
</body>

</html>

就是说我们有了html结构,知识需要webpack将我们的js,css等资源进行编译打包等并插入到相应位置。这就是多页面应用打包了。

我们可以在html-webpack-plugin中的配置项进行配置即可

[code]const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webapck-plugin');

module.exports={

plugins:[

new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename:'index.html',
template:'src/index.html',
chunks:['index']
}),
new HtmlWebpackPlugin({
filename:'floor.html',
template:'src/floor.html',
chunks:['floor']
}),
new HtmlWebpackPlugin({
filename:'delect.html',
template:'src/delect.html',
chunks:['delect']
})
]
}

这里的文件会以template中的html文件为模板文件,然后将文件放到dist目录中,再将名字改为filename中定义的名字,最后注入所依赖的模块,再chunks中分配模板文件依赖的模块,这是个数组,如果他们又公共依赖的模块比如vender.js之类的,直接添加进chunks数组就好了。

三.配置在项目中使用jquery

要想在项目中使用jquery我们需要使用expose-loader进行配置

安装:

[code]npm install expose-loader jquery --save

配置webpack.config.js

[code]module.exports={
module:{
rules:[
test:require.resolve('jquery'),
use:[
{
loader:'expose-loader',
options:'jQuery'
},{
loader:'expose-loader',
options:'$'
}
]
]
}
}

 

整理下最终的webpack.config.js

[code]const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const UglifyJSWebpackPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
entry: {
index: './src/js/index.js',
floor: './src/js/floor.js',
delect: './src/js/delect.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [{
test: /\.m?js$/,
exclude: /(node_modules)|(bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}, {
test: /\.(png|jpe?g|gif)$/,
use: [{
loader: 'url-loader',
options: {
name: '[name].[ext]',
outputPath: 'img',
limit: 1024
}
}]
}, {
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
attrs: ['img:src', 'img:data-src', 'audio:src'],
minimize: true
}
}
}, {
test: /\.(TTF|eot|woff|woff2)$/i,
use: [{
loader: 'file-loader',
options: {
outputPath: 'fonts'
}
}]
},
{
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
options: 'jQuery'
}, {
loader: 'expose-loader',
options: '$'
}]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/index.html',
chunks: ['index']
}),
new HtmlWebpackPlugin({
filename: 'floor.html',
template: 'src/floor.html',
chunks: ['floor']
}),
new HtmlWebpackPlugin({
filename: 'delect.html',
template: 'src/delect.html',
chunks: ['delect']
}),
new webpack.HotModuleReplacementPlugin(),
new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: 'css/[id].css'
}),
new UglifyJSWebpackPlugin({
exclude: /node_modules/
}),
new OptimizeCssAssetsPlugin()
]
}

 

2.开发环境和生产环境分别配置

development(开发环境) 和 production(生产环境) 这两个环境下的构建目标存在着巨大差异。在开发环境中,我们需要:强大的 source map 和一个有着 live reloading(实时重新加载) 或 hot module replacement(热模块替换) 能力的 localhost server。而生产环境目标则转移至其他方面,关注点在于压缩 bundle、更轻量的 source map、资源优化等,通过这些优化方式改善加载时间。由于要遵循逻辑分离,我们通常建议为每个环境编写彼此独立的 webpack 配置

虽然,以上我们将生产环境和开发环境做了略微区分,但是,请注意,我们还是会遵循不重复原则(Don't repeat yourself - DRY),保留一个 "common(通用)" 配置。为了将这些配置合并在一起,我们将使用一个名为 

webpack-merge
 的工具。此工具会引用 "common" 配置,因此我们不必再在环境特定(environment-specific)的配置中编写重复代码。

首先我们创建三个文件webpack.common.js,webpack.dev.js,webpack.prod.js三个文件中分别是公共的打包配置,开发环境的配置以及生产环境的配置。

然后我们更改一下pakage.json文件的脚本配置

[code]"scripts":{
"build":"webpack --config webpack.prod.js",
"start":"webpack-dev-server --open --config webpack.dev.js"
}

安装webpack-merge模块进行配置文件的合并:

[code]npm install webpack-merge --save-dev

1.配置webpack.comcmon.js

[code]const path = require('path');
const HtmlWebpackPlugin=require('html-webpack-plugin');

module.exports={
entry: {
index: './src/js/index.js',
floor: './src/js/floor.js',
delect: './src/js/delect.js'
},
output:{
filename:'[name].bundle.js',
path:path.resolve(__dirname,'dist')
},
module:{
rules:[
{
test:/\.m?js$/,
exclude:/(node_modules)|(bower_components)/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env']
}
}
},
{
test:/\.(png|jpe?g|gif)$/,
use:{
loader:'url-loader',
options:{
name:'[name].[ext],
outputPath:'img',
limits:1024
}
}
},
{
test:/\.html$/,
use:{
loader:html-loader,
options:{
attrs:['img:src','img:data-src','audio:src'],
minimize:true
}
}
},
{
test:/\.(TTF|eot|woff|woff2)$/i,
use:{
loader:'file-loader',
options:{
outputPath:'fonts'
}
}
},
{
test:require.resolve('jquery'),
use:[
{
loader:'expose-loader',
options:'jQuery'
},
{
loader:'expose-loader',
options:'$'
}
]
}
]
},
plugins:[
new HtmlWebpackPlugin({
filename:'index.html',
template:'src/index.html',
chunks:['index']
}),
new HtmlWebpackPlugin({
filename:'floor.html',
template:'src/floor.html',
chunks:['floor']
}),
new HtmlWebpackPlugin({
filename:'delect.html',
template:'src/delect.html',
chunks:['delect']
})

]
}

 

2.配置webpack.dev.js

webpack-dev-server
提供了一个简单的 web server,并且具有 live reloading(实时重新加载) 功能。并且配置HMR,由于webpack内置了HMR所以直接引入webpack,然后在plugins中实例化webpack.HotModuleReplacementWebpackPlugin。并且还要再dev-server中启用hot:true即可使用HMR。hotOnly的意思是如果HMR启动失败,那么每次变更module也不强制自动刷新浏览器。

安装:

[code]npm install webpack-dev-server --save-dev
[code]const common = require('./webpack.dev.js');
const merge = require('webpack-merge');
const webpack = require('webpack');

module.exports =merge(common, {
mode:'development',
devtool:'cheap-module-eval-source-map',
devServer:{
contentBase:'./dist',
port:9000,
hot:true,
hotOnly:true
},
module:{
rules:[
{
test:/\.scss$/,
use:[
'style-loader',
{
loader:'css-loader',
options:{
importLoaders:2
}
},
'postcss-loader',
'sass-loader'
]
}
]
},
plugins:[
new webpack.HotModuleReplacementPlugin()
]

})

 

3.配置webpack.prod.js

[code]const merge = require('webpack-merge');
const common = require('./webpack.common.js');

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin');

module.exports = merge(common,{
mode:'production',
devtool:'cheap-module-source-map',
module:{
rules:[
{
test:/\.scss$/,
use:[
{
loader:MiniCssExtractPlugin.loader,
options:{
publicPath:'../'
}
},
{
loader:'css-loader',
options:{
importLoaders:2
}
},
'postcss-loader',
'sass-loader'
]
}
]
},
plugins:[
new CleanWebpackPlugin(),
new UglifyJsWebpackPlugin({
exclude:/node_modules/
}),
new MiniCssExtractPlugin({
filename:'css/[name].css',
chunkFilename:'css/[id].css'
}),
new OptimizeCssAssetsPlugin(),
]

})

 

以下附上所有的安装的模块,测试如果嫌安装各种loader和plugin麻烦则可以直接使用package.json文件,然后运行npm install即可:

pakage.json

[code]{
"name": "situation-wp",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"build": "webpack  --config webpack.prod.js",
"start": "webpack-dev-server --open --config webpack.dev.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.4.5",
"@babel/preset-env": "^7.4.5",
"autoprefixer": "^9.6.0",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.0.0",
"cssnano": "^4.1.10",
"cssnano-preset-advanced": "^4.0.7",
"extract-loader": "^3.1.0",
"file-loader": "^4.0.0",
"html-loader": "^0.5.5",
"mini-css-extract-plugin": "^0.7.0",
"node-sass": "^4.12.0",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"postcss-aspect-ratio-mini": "^1.0.1",
"postcss-cssnext": "^3.1.0",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
"postcss-px-to-viewport": "^1.1.0",
"postcss-url": "^8.0.0",
"postcss-viewport-units": "^0.1.6",
"postcss-write-svg": "^3.0.1",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"uglifyjs-webpack-plugin": "^2.1.3",
"url-loader": "^2.0.0",
"webpack": "^4.34.0",
"webpack-cli": "^3.3.4",
"webpack-dev-server": "^3.7.2",
"webpack-merge": "^4.2.1",
"html-webpack-plugin": "^3.2.0",
},
"dependencies": {
"echarts": "^4.2.1",
"expose-loader": "^0.7.5",
"jquery": "^3.4.1"
}
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: