现在,命令行工具搭建好了,那么接下来的任务就是搞一个项目模板,奥力给!
项目模板
由于我们要做的项目模板是一个单页面应用,所以首先要借助webapack和webpack-cli来帮我们构架项目。所谓单页面应用,可以理解为只有一个html文件
第一步:创建文件夹,并初始化pakage.json文件
$ mkdir zaoren-demo && cd zaoren-demo
$ npm init -y
接着,构建如下目录结构
zaoren-demo
|- package.json
|- /src
|- index.js
|- index.html
index.js
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello World';
return element;
}
document.body.appendChild(component());
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>zaoren-demo</title>
<div id="root"></div>
</head>
<body>
</body>
</html>
第二步,安装HTMLWebpackPlugin插件,创建webpack配置文件
$ npm install html-webpack-plugin --save-dev
创建build目录,新建webpack.config.js(之后将区分dev和prod环境)
webpack.config.js
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, '../dist')
},
mode: 'development',
plugins: [
new HTMLWebpackPlugin({
// 用于生成的HTML文档的标题
title: 'Webpack 开发环境配置',
// webpack 生成模板的路径
template: './src/index.html'
})
]
}
package.json
在package.json的scripts中新增build命令找到webpack的配置文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config build/webpack.config.js"
},
执行 npm run build 可以看到在dist文件夹中生成了html和js文件,打开html看到 ‘Hello World’
这样,一个最基本的webpack程序就跑通了,也许你会觉得奇怪,为什么不和以前一样直接在html中引入js文件了呢?而是费劲去安装一个插件自动引入? 因为我们的单页面应用程序,之后会将所有的.vue,.jsx等其他格式的文件解析成浏览器能识别的js文件,从文件个数来说是不确定的,html页面依赖的js文件有可能是一个有可能是多个,从文件的名称来说,我们的文件通过hash值来命名(为了做缓存)是不确定的,所以不可能手动去引入js。
第三步,区分prod和dev环境
我们项目开发完之后,是需要打包上线的,而我们在开发阶段和打包上线阶段使用的一些插件是有区别的,所以需要我们将webpack的配置文件分成两个,修改build文件夹如下
zaoren-demo
|- /build
|- webpack.base.config.js
|- webpack.dev.config.js
|- webpack.prod.config.js
webpack.base.config.js
将webpack.config.js更名为wepack.base.config.js,相当于是dev,prod的基础配置,dev和prod环境的webpack配置文件将在此基础上拓展。
webpack.dev.config.js
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.config.js');
// 重新封装resolve,定位到项目的根目录
const resolve = (dir) => path.join(__dirname, '..', dir);
module.exports = merge(baseWebpackConfig, {
devtool: 'eval-source-map',
mode: 'development',
});
webpack.prod.config.js
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.config.js');
module.exports = merge(baseWebpackConfig, {
mode: 'production',
});
package.json
对应的,需要在package.json中区分一下运行dev和prod环境的命令,我们先再完善一下webpack的配置再来修改package.json
第四步:拓展webpack.base.config.js
1.使用clean-webpack-plugin插件
当你多次build之后会发现,dist文件夹中将每一次的打包结果都保存下来了,clean-wepack-plugin就是用来保存最后一次生成结果的插件
安装插件:
$ npm install clean-webpack-plugin --save-dev
...
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
...
plugins: [
...,
// 用法:new CleanWebpackPlugin(paths [, {options}])
new CleanWebpackPlugin()
]
}
2.为项目添加模块解析规则
都说webpack的作用是将浏览器不能识别的文件如:less,sass,vue,jsx解析成浏览器能解析的css,js,html,所以就需要配置很多的loader去帮助我们解析各种文件(modules和plugins同级)。
安装loader
$ npm install clean-webpack-plugin --save-dev
...
module.exports = {
...
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
// 开启 sourceMop
sourceMap: true,
},
},
],
},
{
test: /\.less$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader', // translates CSS into CommonJS
},
{
loader: 'less-loader', // compiles Less to CSS
},
],
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader',
],
},
],
},
}
下面,尝试着在src目录下新建index.less文件,并且在index.js中导入, 运行npm run build
index.js
import './index.less'
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello World';
return element;
}
document.body.appendChild(component());
3.安装react和react-dom
$ npm install react react-dom --save-dev
我们修改我们的index.js代码,尝试着用jsx语法来写我们的项目
import React from "react";
import ReactDom from "react-dom";
var a = 1;
var b = 2;
c = a + b;
ReactDom.render(
<h1>hello,word!</h1>,
document.getElementById('root')
);
npm run build 发现报错了, 因为webpack只识别JavaScript文件,而且只能编译es5版本的JavaScript语法。实际上,我们使用ES2015,以及jsx的语法糖,webpack它根本不认识啊。怎么办?webpack 可以使用 loader 来预处理文件。 所以我们需要使用Babel转译为ES5。
4.babel的配置
安装依赖,并且在项目根目录新建.babelrc
$npm install @babel/core babel-loader @babel/preset-env @babel/preset-react --save-dev
.babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [],
}
webpack.base.config.js中的rules增加babel-loader
...
module.exports = {
...
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, '../src'),
use: ['babel-loader'],
},
...
],
},
}
再次执行npm run build,打包成功,打开html页面,显示hello world,至此,react和 ES2015+ 开发环境已经搭建完成了!接下来要做的事情就搭建更完善的开发环境了。
第五步:拓展webpack.dev.config.js
之前我们说过,我们使用webpack的时候,专门对dev和prod环境做了区分,那么需要我们在pakage.json中用不同的scripts来运行我们的代码
pakage.json
"build": "webpack --config build/webpack.prod.conf.js --mode=production",
"dev": "webpack --open --config build/webpack.dev.conf.js --mode=development"
使用 webpack-merge插件将webpack.base.config.js的配置合并到dev
$npm install webpack-merge --save-dev
使用 HotModuleReplacementPlugin ,FriendlyErrorsWebpackPlugin 插件
安装依赖(HotModuleReplacementPlugin不需要安装,webpack内置的)
$npm install FriendlyErrorsWebpackPlugin --save-dev
HotModuleReplacementPlugin 插件帮助我们在开发过程中实现热更新,修改代码后不需要手动去刷新页面。
FriendlyErrorsWebpackPlugin 插件 能够更好在终端看到webapck运行的警告和错误
webpack.dev.config.js文件内容
const path = require('path');
const webpack = require('webpack');
const merge = require('webpack-merge');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const baseWebpackConfig = require('./webpack.base.config.js');
// 重新封装resolve,定位到项目的根目录
const resolve = (dir) => path.join(__dirname, '..', dir);
// 使用merge将原来webpack.base.config.js中的配置读取到
module.exports = merge(baseWebpackConfig, {
mode: 'development',
plugins: [
new webpack.HotModuleReplacementPlugin(),
new FriendlyErrorsWebpackPlugin(),
],
});
启用调试工具 Source Map
由于我们使用了webpack打包之后,生成的文件只有html和main.js,代码出问题的时候,很难进行调试,所以,我们需要启用调试工具Source Map
在webpack.dev.config.js 中添加devtool属性
...
module.exports = {
mode: 'development',
+ devtool: 'eval-source-map',
...
}
这样就可以在google的调试中ctrl+p然后搜索相应的文件名,去调试我们的代码啦!
使用 webpack-dev-server 插件
不得不说,这是一个很强大的插件。能帮助我们:1.在本地启动一个服务 2.项目实现热更新 3.集成了 http-proxy-middleware 中间件做本地代理,解决开发阶段跨域问题
安装插件:
$ npm install --save-dev webpack-dev-server
webpack.dev.config.js中添加devserver配置(和devtool同级)
devServer: {
// 必须配置的选项,服务启动的目录,默认为根目录
contentBase: './dist',
// 使用热加时需要设为true
hot: true,
inline: true,
host: 'localhost',
port: 8000,
historyApiFallback: {
disableDotRule: true,
},
// 出现错误时是否在浏览器上出现遮罩层提示
overlay: true,
stats: 'errors-only',
// 设置接口请求代理,更多 proxy 配置请参考 https://github.com/chimurai/http-proxy-middleware#options
proxy: {},
},
现在,我们需要使用webpack-dev-server 来启动我们的项目,在pakage.json中修改我们的启动命令
"dev": "webpack-dev-server --open --config build/webpack.dev.config.js --mode=development"
运行 npm run dev,会发现,我们在本地的8000端口,启了一个服务来运行我们的项目,修改’hello world’为’hello world1’之后,页面自动会刷新!!!那么webpack-dev-server就集成好了(devserver中的proxy的属性非常重要,能帮助我们在开发阶段解决跨域问题,之后会说到)
第六步:集成Eslint来规范代码
当我们一个项目由多个人开发的时候,代码风格,书写规范就显得尤其重要。Eslint就是来帮助我们规范代码,并且能帮助我们避免低级错误,自动格式化代码。
1.安装eslint-loader
npm install --save-dev eslint-loader
2.安装eslint
npm install --save-dev eslint
3.在webpack.base.config.js中添加eslint-loader
在原来的babel-loader后加上一个eslint-loader
{
test: /\.js/,
include: path.resolve(__dirname, '../src'),
use: ['babel-loader', 'eslint-loader'],
},
4.安装babel-eslint
npm install babel-eslint --save-dev
5.在项目根目录新建.eslintrc.js文件
我们这里借用Airbnb的校验规则,所以需要安装一些依赖
- eslint-plugin-jsx-a11y
- eslint-plugin-react
- eslint-plugin-import
- eslint-plugin-react-hooks
由于这些依赖的对版本号有一定的要求,所以我们使用eslint --init 来帮我们创建Airbnb的规则,自动帮我们下载正确的依赖版本
eslint --init
.eslintrc.js
const path = require('path');
const resolve = (dir) => path.resolve(__dirname, dir);
module.exports = {
env: {
"browser": true,
"es6": true,
"node": true
},
parser: 'babel-eslint',
extends: [require.resolve('eslint-config-airbnb')],
plugins: ['react', 'jsx-a11y', 'import', 'react-hooks'],
parserOptions: {
ecmaFeatures: {
legacyDecorators: true,
},
},
settings: {
'import/resolver': {
webpack: {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
config: {
resolve: {
alias: {
'@': resolve('src')
},
},
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
},
globals: {
window: true,
document: true,
XMLHttpRequest: true,
fetch: true,
AMap: true,
ENV: true,
},
rules: {
'max-len': ['warn', { code: 120 }],
// "no-nested-ternary": 'warn',
'react/prefer-stateless-function': 'off', // 只有render
'react/jsx-filename-extension': 'off', // .jsx
'react/jsx-first-prop-new-line': 'off', // 换行
'react/sort-comp': 'off',
'react/jsx-closing-tag-location': 'off',
'react/no-did-mount-set-state': 'off', // DidMount中可以使用setState
'import/extensions': 'off', // 扩展名
'import/prefer-default-export': 'off',
'jsx-a11y/no-static-element-interactions': 'off',
'jsx-a11y/click-events-have-key-events': 'off',
// warn
'no-plusplus': ['warn', { allowForLoopAfterthoughts: true }],
'class-methods-use-this': 'off',
'consistent-return': 'off',
'no-unused-expressions': 'off',
'no-param-reassign': 'off',
'no-mixed-operators': 'off',
'no-shadow': 'off',
'react/prop-types': 'off',
'react/forbid-prop-types': 'off',
'react/no-array-index-key': 'off',
'react/no-string-refs': 'off',
'jsx-a11y/anchor-has-content': 'off',
'jsx-a11y/anchor-is-valid': 'off',
'jsx-a11y/no-noninteractive-element-interactions': 'off',
'react/destructuring-assignment': 'off',
'react-hooks/rules-of-hooks': 'warn',
'react-hooks/exhaustive-deps': 'warn',
'react/jsx-props-no-spreading': 'off',
'react/jsx-one-expression-per-line': 'off',
'@typescript-eslint/explicit-member-accessibility': 'off',
'no-unused-vars': 'warn',
"linebreak-style": 'off',
"no-console": "off",
},
};
6.vscode的Extensions中安装Eslint插件,并且开启autoFix
文件 -> 首选项 -> 设置 搜索 eslint,开启autofix
打开我们的index.js,Ctrl+S保存时自动将我们var声明的变量 改成了const
小结
1.完成了命令行工具的搭建,并且发布到npm官网
2.搭建项目模板,区分dev和prod环境,集成开发阶段的插件
3.接下来将完善webpack.prod.config.js的配置,来帮助我们的项目打包上线。
完整配置文件参考 https://github.com/zaoren/shuju-zaoren-cli/tree/Tag1.0.0/config