序
接上篇,命中注定拯救前端的,应该是 webpack (一) - 掘金 (juejin.cn)
我们接着来完善开发环境。
由于 npm run start 有歧义,我们将它更改为 npm run build.
CSS 文件单独引入
上一篇我们已经完成了 less 构建,但打包后,我们在 dist/index.html 并没有看到 css 的引入。由于我们是在 js 文件中引入的 less ,所以 webpack 将它们打包到了一起。
这样做危险的一点在于,如果我们有时候需要设定 script 的 defer 属性,那么 最开始将没有页面样式。
因此,我们将 css 单独打包。
npm install mini-css-extract-plugin -D
在 webpack.config.js 中引入:
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
...
plugins:[ // 配置插件
new HtmlWebpackPlugin({
template: './index.html',
scriptLoading: 'blocking',
}),
new MiniCssExtractPlugin({ // 添加插件
filename: '[name].[hash:8].css'
}),
new webpack.HotModuleReplacementPlugin()
],
...
use: [
MiniCssExtractPlugin.loader,//将处理好的 css 通过 style 标签的形式添加到页面上
'css-loader',//将 CSS 转化成 webpack 能够识别的数据
'postcss-loader',//自动添加 CSS3 部分属性的浏览器前缀
'less-loader',// 将 less 文件转化为 css
],
....
此时运行 npm run build。则可以在 dist->index.html 文件夹下看到 css 文件的引入了。
ES6 构建
尽管 ES6 的语法在开发中已经较为广泛的被使用,但并不是每个浏览器都能识别。
Babel 的出现解决了这个问题。
官网说明:
Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
如:
// Babel 输入: ES2015 箭头函数 [1, 2, 3].map(n => n + 1); // Babel 输出: ES5 语法实现的同等功能 [1, 2, 3].map(function(n) { return n + 1; });我们可以看到,他的转换还有一个 缺陷, map 等方法并不会进行转换,因此我们需要:
通过 Polyfill 方式在目标环境中添加缺失的特性 (通过引入第三方 polyfill 模块,例如 core-js)
因此,我们需要安装:
npm i -D babel-loader @babel/preset-env @babel/core
npm i -D @babel/polyfill
这时,我们在 src/index.js 文件下写入:
let arr = ['--星期一--','--星期二--','--星期三--','--星期四--','--星期五--','--星期六--','--星期日--',]
arr.forEach(element => {
console.log(element);
});
将 webpack.config.js 中的 mode 改为 ’none‘, 然后直接运行 npm run build。
可以在 dist 中 bundle.js 文件夹下看到:
并没有发生改变。
然后,我们在 webpack.config.js 中的 module 下的 rules 添加:
{
test: /\.js$/,
use:
[{
loader:'babel-loader',
options: {
presets: [
'@babel/preset-env'// babel 启动插件
],
}
}],
},
运行 npm run build。
此时我们发现,bundle.js 的图片变成了如下:
将箭头函数改为了普通函数。
polyfill 暂时无法验证,所以不用。
支持 React 开发
首先安装 react 相关包
npm i react react-dom
Babel通过@babel/preset-react包来构建React应用
npm i @babel/preset-react -D
然后在 webpack.config.js 中对 js 的配置稍作改动,注意,这里要将 mode 改回为 ‘development’ 否则会报错。
{
test: /\.(jsx|js)$/,
use:
[{
loader:'babel-loader',
}],
},
你可能发现了,配置里的 preset 选项不见了。为了避免页面冗余,我新建了一个文件 :.babelrc, 在这个文件中写入了 预设配置。
{
"presets": ["@babel/preset-env","@babel/preset-react"]
}
此时配置完毕,然我们来进行验证吧!
新建 app.jsx 文件,写入熟悉方便的 react 代码。
import React from 'react';
export default class App extends React.Component{
handleClick = () =>{
console.log('click!')
}
render(){
return <div onClick={this.handleClick}>ClickTest</div>
}
}
然后删除 index.js 原有的代码,写入:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app.jsx';
import "./index.less"
ReactDOM.render(<App />,document.querySelector("#root"))
此时已经没有必要用 showDate.js 了,因此我删除了 showDate.js
目前我的目录结构如下:
然后 npm run dev 运行项目,可以看到界面上出现了 ClickTest ,点击即可在控制台进行对应输出。
提高开发效率的一些配置- Resove
alias -- 让导入模块变得更简单
用 react 开发的时候,我经常会写一些组件,放在 src/component 文件夹下。
在进行页面开发时,如果页面层级过深,导入的时候则非常容易出错。使用 resove 中的 alias 可以解决这个问题。
现在 src/component 文件,在文件下创建 MyDiv 文件夹及 index.jsx 文件,写入如下代码:
import React from 'react';
import "./index.less"
export default class App extends React.Component{
render(){
return <div className='my-div'></div>
}
}
创建 index.less 文件,写入如下代码:
.my-div{
width: 200px;
height:200px;
border:1px solid rgb(228, 218, 217);
margin:20px auto;
}
此时,如果直接引入改组件,则在 src/app.jsx 写入如下代码:
import React from 'react';
import MyDiv from './component/MyDiv/index.jsx'
export default class Index extends React.Component{
handleClick = () =>{
console.log('click!')
}
render(){
return <div onClick={this.handleClick}>
<MyDiv></MyDiv>
</div>
}
}
此时,如果我们在 webpack.config.js 添加如下配置:
resolve:{
alias:{
'@component': path.resolve(__dirname, 'src/component'),
}
},
导入 MyDiv 的语句则可以变成下面这样:
import MyDiv from '@component/MyDiv/index.jsx'
当文件层级较深的时候,这种写法会方便很多。
extensions -- 不用再写拓展名
添加以下配置:
...
resolve:{
alias:{
'@component': path.resolve(__dirname, 'src/component'),
},
extensions:['.js','.jsx'],
},
...
配置完这个后,webpack 会读取配置指定的拓展名尝试访问文件是否存在。没有配置这个之前,当 访问 MyDiv 时,默认访问 index.js,所以必须指定 MyDiv/index.jsx。
配置完这个后,如果 index.js 不存在,它会继续找 index.jsx 。这样引入的时候我们可以写成:
import MyDiv from '@component/MyDiv'
看着简洁清晰了许多。
像 antd 一样引入自己的组件
如果我们组件写的比较多,引入多个组件,要是每个组件都占一行,未免有些冗余。
我们可以这样做,在 component 下新建 index.js 文件。
我已经重新写入了一个 MyButton 组件,在此不再累述。、
写入:
export { default as MyDiv } from './MyDiv';
export { default as MyButton} from './MyButton';
此时我们就可以像这样在 app.jsx 中引入组件了:
import { MyDiv, MyButton } from '@component';
是不是看起来更加直观、整洁了呢?
避免 Class 名重复 - CSS Modules
完成以上工作后,我发现我两个组件中的样式有点串。
原来,我忘记修改 复制文件的 index.less 文件中的 class 名,而直接修改了样式,它们打包到一个 css 文件夹下,所以两个样式都对原组件生效了。
这可不行!!!总会有偷懒不想写到公共样式文件的时候,总会有复制粘贴的时候!
这个时候怎么办呢?
开启 css-loader 的 modules, 它会为每个 class 生成唯一标识。
文章发表后才发现问题的,所以后续截图有误差,实际的 css-loader 需要添加 modules.
{
test: /.less$/,
use: [
MiniCssExtractPlugin.loader,//将处理好的 css 通过 style 标签的形式添加到页面上
{
loader:'css-loader',
options: {
modules: true
}
},//将 CSS 转化成 webpack 能够识别的数据
'postcss-loader',//自动添加 CSS3 部分属性的浏览器前缀
'less-loader',// 将 less 文件转化为 css
{
loader: 'thread-loader', // 开启多进程打包
options: {
worker: 3,
}
}
],
include:[path.resolve(__dirname, 'src')]
},
图片处理
新建 src-assets 文件夹,放入一张图片。
配置别名:
...
alias:{
'@component': path.resolve(__dirname, 'src/component'),
'@assets': path.resolve(__dirname, 'src/assets'),
},
...
我们可以尝试一下,直接引入图片会报错。
因此我们需要下载:
npm install file-loader -D
配置 webpack.config.js
...
{
test: /\.(jpe?g|png|gif)$/i, // 匹配图片文件
use:[
{
loader:'file-loader',
options: {
limit: 10240,//如果文件小于限制的大小,则会返回 base64 编码
}// 使用 file-loader
}
]
}
...
然后就可以正常显示图片了。
结语
到这里,一个基于 React + less + es6 的项目搭建就基本完成了。
但是这远远没有触及到 webpack 的核心功能,及项目的性能优化。
下篇文章,我们继续。