您的位置:首页 > Web前端 > Vue.js

使用webpack+Vue构建模块化项目

2019-09-10 13:03 651 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_36823300/article/details/100695856

webpack+Vue是一种常用的构建模块化项目的组合。webpack是模块打包工具,它可以分析项目结构,找到JavaScript模块以及其他浏览器不能直接运行的扩展语言(如Scss、TypeScript 等),并将其打包为合适的格式供浏览器使用。

webpack

webpack将项目作为一个整体,并指定一个主文件(如main.js)。webpack可以从主文件开始找到项目中的依赖文件,然后使用loaders处理它们,最后打包成浏览器可以识别的JavaScript文件。

webpack的工作原理如下所示。

webpack 的工作原理

1)创建webpack项目 

可以使用npm命令创建webpack项目。

首先创建一个webpack项目文件夹webpackProject,打开命令行窗口,执行下面的命令,初始化webpack项目。

[code]cd webpackProject
npm init

在该命令执行过程中会提示输入一系列的项目版本信息,包括包名(package name)、版本(version)、描述信息(description)、主文件(entry point)、测试命令(test command)、git库(git repository)、关键字(keywords)、作者(author)、许可证号(license)等,这些信息都不是必填的,全部回车即可。最后会提示用户即将创建一个package.json文件,这是一个标准的npm说明文件。确认屏幕上显示的信息,如果不需要修改,则单击回车键。

(也可以使用 npm init -y 这个命令来一次生成package.json文件,这样终端不会询问你问题)

初始化 webpack 项目

 执行成功后,可以看到 webpackProject 文件夹下生成了一个 package.json 文件。

接下来就可以执行下面的命令,在项目中安装 webpack 作为依赖包。

如果你想一步到位的话,就把全局webpack和本地项目webpack全都先装了,因为后面一些模块会用到。安装本地项目webpack时把webpack-cli也装上,因为webpack模块把一些功能分到了webpack-cli模块,安装命令如下所示。

[code]npm install webpack -g
npm install webpack webpack-cli -g

如果执行的过程中报错,则可以执行下面的命令清除webpack的缓存。

[code]npm cache clear --force

也可能是由于npm的版本不匹配。若遇到npm ERR! code EPERM错误时,执行下面的命令将npm降级至5.0.3版本,解决问题。

[code]npm install -g npm@5.0.3

在项目中安装 webpack 后,在项目文件夹下会生成一个 node_modules 子文件夹。

批量下载

我们从网上下载某些代码,发现只有package.json,没有node_modules文件夹,这是我们需要通过命令重新下载这些js库。

进入目录(package.json所在的目录),输入命令:

[code]npm install

此时,npm会自动下载package.json中依赖的js库。 

2)新建文件

在 webpackProject文件夹中新建两个文件夹,分别为src文件夹和dist文件夹,接下来再创建三个文件:

  • index.html    --放在dist文件夹中;
  • hello.js    --放在src文件夹中;
  • index.js    --放在src文件夹中;

此时,项目结构如下所示。

我们在index.html中写下html代码,它的作用是为了引入我们打包后的js文件。

[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>Webpack Project</title>
</head>
<body>
<div id="root"></div>
<script src="bundle.js"></script>   <!-- 这是打包之后的js文件,我们暂时命名为bundle.js -->
</body>
</html>

 我们在hello.js中导出一个模块。

[code]// hello.js
module.exports = function() {
let hello = document.createElement('div');
hello.innerHTML = "Long time no see!";
return hello;
};

 然后在index.js中引入这个模块(hello.js)。

[code]// index.js
const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());

上述操作就相当于我们把hello.js模块合并到了index.js模块,之后我们打包时就只需把index.js模块打包成bundle.js,然后供index.html引用即可,这就是最简单的webpack打包原理。 

3)开始进行webpack打包

在终端中使用如下命令进行打包。

[code]// webpack全局安装的情况下
webpack src/index.js -o dist/bundle.js

上述就相当于把src文件夹中的index.js文件打包到dist文件夹下的bundle.js,这时就生成了bundle.js供index.html文件引用。

结果如下:

黄色部分的警告的意思是,没有设置模式,有开发模式和生产模式两种。--mode development是开发模式,开发模式输出的不经过压缩,是平时我们写代码的形式,--mode production是生产模式,生产模式输出的是经过压缩过的,例如我们在Vue官方上下载的vue.min.js的样式。(示例:webpack src/index.js -o dist/bundle.js --mode development)

可以看出webpack同时编译了index.js和hello.js,现在打开index.html,可以看到如下结果:

这样,我们就成功使用webpack进行打包了。

4)通过配置文件来使用webpack

webpack执行时,除了在命令行传入参数外,还可以通过配置文件来指定参数。默认情况下,会搜索当前目录的webpack.config.js文件,这是一个node.js模块,返回一个json格式的配置信息对象。也可以通过--config选项来指定配置文件。

下面是webpack.config.js文件的例子(只涉及入口配置(相当于我们的index.js,从它开始打包)和出口配置(相当于我们打包生成的bundle.js))。

[code]// webpack.config.js
module.exports = {
entry: __dirname + "/src/index.js", // 入口文件
output: {
path: __dirname + "/dist",  //打包后的文件存放的地方
filename: "bundle.js"   //打包后输出文件的文件名
}
}

注:__dirname是node.js中的一个全局变量,它指向当前执行脚本所在的目录。 

参数说明如下。

  • entry:用于指定入口文件。
  • output:用于指定打包结果。path指定输出的文件夹,filename指定打包结果文件的名称。
  • module:定义对模块的处理逻辑。当需要加载的文件匹配test的正则时,调用后面的loader对文件进行处理。

但平时我们看到的脚手架配置也比较喜欢采用node.js的path模块来处理绝对路径,所以我们也可以采用如下写法,和上述的效果是一样的。

[code]// webpack.config.js
const path = require('path');
module.exports = {
entry: path.join(__dirname, "/src/index.js"),   // 入口文件
output: {
path: path.join(__dirname, "/dist"),    // 打包后的文件存放的地方
filename: "bundle.js"   // 打包后输出文件的文件名
}
}

注:path.join的功能是拼接路径片段。 

有了这个配置文件,我们只需在终端中运行webpack命令就可进行打包,这条命令会自动引用webpack.config.js文件中的配置选项,示例如下:

这样我们就可以更加方便地打包了。 

 

5)更智能的打包方式

我们现在只在终端中使用webpack命令来进行打包,以后在打包的同时还要进行更多的操作,我们要把这些命令都集成起来,这时候之前的package.json文件就派上用场了。

原来的package.json文件大概如下所示:

[code]{
"name": "webpackproject",
"version": "1.0.0",
"description": "My first webpack project",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.39.3",
"webpack-cli": "^3.3.8"
}
}

修改如下:

[code]{
"name": "webpackproject",
"version": "1.0.0",
"description": "My first webpack project",
"main": "index.js",
"scripts": {
"start": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.39.3",
"webpack-cli": "^3.3.8"
}
}

注:package.json中的script会按你设置的命令名称来执行对应的命令。

这样我们就可以在终端中直接执行 npm start 命令来进行打包,start 命令比较特殊,可以直接 npm 加上 start 就可以执行,如果我们想起其他的名称,如 build 时,就需要使用 npm run 加上 build,即 npm run build 命令。

现在我们执行 npm start 命令:

 成功打包。

 

6)webpack-dev-server配置本地服务器

webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js构建,它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖:

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

devServer作为webpack配置选项中的一项,以下是它的一些配置选项:

  • contentBase:设置服务器所读取文件的目录,当前我们设置为"./dist"
  • port:设置端口号,如果省略,默认为8080
  • inline:设置为true,当源文件改变时会自动刷新页面
  • historyApiFallback:设置为true,所有的跳转将指向index.html

现在我们把这些配置加到 webpack.config.js 文件上,如下所示:

[code]// webpack.config.js
const path = require('path');
module.exports = {
entry: __dirname + "/src/index.js", // 入口文件
output: {
path: __dirname + "/dist",  //打包后的文件存放的地方
filename: "bundle.js"   //打包后输出文件的文件名
},
devServer: {
contentBase: "./dist",  // 本地服务器所加载文件的目录
port: "8088",   // 设置端口号为8088
inline: true, // 文件修改后实时更新
historyApiFallback: true    // 不跳转
}
}

我们继续在 package.json 文件中添加启动命令:

[code]{
"name": "webpackproject",
"version": "1.0.0",
"description": "My first webpack project",
"main": "index.js",
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --open"
},
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.39.3",
"webpack-cli": "^3.3.8",
"webpack-dev-server": "^3.8.0"
}
}

我们把start命令名称改成了build,这样比较语义化,平时的脚手架也多数采用这个名称,我们用dev(development的缩写,意指开发环境)来启动本地服务器,webpack-dev-server 就是启动服务器的命令,--open 是用于启动完服务器后自动打开浏览器,这时候我们自定义命令方式的便捷性就体现出来了,可以多个命令集成在一起运行,即我们定义了一个dev命令名称就可以同时运行了 webpack-dev-server 和 --open两个命令。

现在我们在终端输入 npm run dev 运行服务器:

 这样我们就可以在http://localhost:8088中查看页面(退出服务器,可使用 ctrl+c 后,再输入y确认,即可退出服务器运行)

 

7)Source Maps调试配置

source Map用来保存我们错误出现的位置。

通过如下配置,我们会在打包时生成对应于打包文件的 .map 文件,使得编译后的代码可读性更高,更易于调试。

[code]// webpack.config.js
const path = require('path');
module.exports = {
entry: __dirname + "/src/index.js", // 入口文件
output: {
path: __dirname + "/dist",  //打包后的文件存放的地方
filename: "bundle.js"   //打包后输出文件的文件名
},
devServer: {
contentBase: "./dist",  // 本地服务器所加载文件的目录
port: "8088",   // 设置端口号为8088
inline: true, // 文件修改后实时更新
historyApiFallback: true    // 不跳转
},
devtool: 'source-map'   // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
}

配置好后,我们再次运行 npm run build 进行打包,这时我们会发现在dist文件夹中多出了一个 bundle.js.map 文件,如下所示:

 如果我们的代码存在bug,在浏览器的调试工具中会提示错误出现的位置,这就是 devtool: 'source-map' 配置项的作用。

 

8)Loaders

loaders是webpack最强大的功能之一,通过不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,例如把 scss 转为 css ,将ES6、ES7等语法转化为当前浏览器能识别的语法,将JSX转化为JS等多项功能。

Loaders需要单独安装并且需要在 webpack.config.js 中 modules 配置项进行配置,Loaders的配置包括以下几方面:

  • test:一个用以匹配loaders所处理文件的扩展名的正则表达式(必须)
  • loader:loader的名称(必须)
  • include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选)
  • options:为loaders提供额外的设置选项(可选)

(1)配置css-loader

如果我们需要加载一个css文件,需要安装配置 style-loader 和 css-loader:

[code]npm install style-loader css-loader --save-dev
[code]// webpack.config.js
const path = require('path');
module.exports = {
entry: __dirname + "/src/index.js", // 入口文件
output: {
path: __dirname + "/dist",  //打包后的文件存放的地方
filename: "bundle.js"   //打包后输出文件的文件名
},
devServer: {
contentBase: "./dist",  // 本地服务器所加载文件的目录
port: "8088",   // 设置端口号为8088
inline: true, // 文件修改后实时更新
historyApiFallback: true    // 不跳转
},
devtool: 'source-map'   // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正则匹配以.css结尾的文件
use: ['style-loader', 'css-loader'] // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
}
]
}
}

我们在src文件夹下新建 css 文件夹,该文件夹内新建 style.css 文件:

[code]/* style.css */
body {
background: gray;
}

在 index.js 中引用它:

[code]// index.js
import './css/style.css';   // 导入css

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());

这时候我们运行 npm run dev,会发现页面背景变成了灰色。

2)配置less

安装less-loader,因为less-loader依赖于less,所以我们需要借助于less编译。

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

然后配置less的rules: 

[code]// webpack.config.js
const path = require('path');
module.exports = {
entry: path.join(__dirname, "/src/index.js"), // 入口文件
output: {
path: path.join(__dirname, "/dist"),  //打包后的文件存放的地方
filename: "bundle.js"   //打包后输出文件的文件名
},
devServer: {
contentBase: "./dist",  // 本地服务器所加载文件的目录
port: "8088",   // 设置端口号为8088
inline: true, // 文件修改后实时更新
historyApiFallback: true    // 不跳转
},
devtool: 'source-map',   // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正则匹配以.css结尾的文件
use: ['style-loader', 'css-loader'] // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
},
{
test: /\.less$/,    // 正则匹配以.less结尾的文件
use: ['style-loader', 'css-loader', 'less-loader']  //需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
}
]
}
}

3)配置sass

首先,在项目中添加一个.npmrc文件,在里面添加淘宝镜像源:

[code]sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
registry=https://registry.npm.taobao.org

 然后安装sass-loader(因为sass-loader依赖于node-sass,所以还要安装node-sass):

[code]npm install sass-loader node-sass --save-dev

增加sass的rules:

[code]// webpack.config.js
const path = require('path');
module.exports = {
entry: path.join(__dirname, "/src/index.js"), // 入口文件
output: {
path: path.join(__dirname, "/dist"),  //打包后的文件存放的地方
filename: "bundle.js"   //打包后输出文件的文件名
},
devServer: {
contentBase: "./dist",  // 本地服务器所加载文件的目录
port: "8088",   // 设置端口号为8088
inline: true, // 文件修改后实时更新
historyApiFallback: true    // 不跳转
},
devtool: 'source-map',   // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正则匹配以.css结尾的文件
use: ['style-loader', 'css-loader'] // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
},
{
test: /\.less$/,    // 正则匹配以.less结尾的文件
use: ['style-loader', 'css-loader', 'less-loader']  //需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
},
{
test: /\.(scss|sass)$/, //正则匹配以.scss和.sass结尾的文件
use: ['style-loader', 'css-loader', 'sass-loader']  //需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
}
]
}
}

 在css文件夹中新建blue.scss文件:

[code]// blue.scss
$blue: blue;
body {
color: $blue;
}

在index.js中引入blue.scss:

[code]// index.js
import './css/style.css';   // 导入css
import './css/blue.scss';   // 导入scss

const hello = require('./hello.js');
document.querySelector("#root").appendChild(hello());

这时 npm run dev 重新启动服务器,就能得到结果了。 

 注意:若不使用淘宝源,则会出现很多难以解决的错误,为了节省时间,建议使用.npmrc文件添加淘宝镜像源。

4)Babel

Babel是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:

  • 让你能使用最新的JavaScript代码(ES6、ES7…),而不用管新标准是否被当前使用的浏览器完全支持
  • 让你能使用基于JavaScript进行了拓展的语言,比如React的JSX

(1)Babel的安装与配置

Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析ES6的babel-preset-env包和解析JSX的babel-preset-react包)

[code]npm install babel-core babel-loader babel-preset-env babel-preset-react --save-dev

babel-preset-env的env表示是对当前环境的预处理,而不是像以前使用的babel-preset-es2015只能针对某个环境。

执行成功后,在webpack项目文件夹下的package.json文件的内容如下:

[code]{
"name": "webpackproject",
"version": "1.0.0",
"description": "My first webpack project",
"main": "index.js",
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --open"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^8.0.6",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"css-loader": "^3.2.0",
"less": "^3.10.3",
"less-loader": "^5.0.0",
"node-sass": "^4.12.0",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.8",
"webpack-dev-server": "^3.8.0"
},
"dependencies": {}
}

可以看到devDependencies包含babel-core、babel-loader、babel-preset-env和babel-preset-react等插件。

我们继续配置webpack.config.js文件:

[code]// webpack.config.js
const path = require('path');
module.exports = {
entry: path.join(__dirname, "/src/index.js"), // 入口文件
output: {
path: path.join(__dirname, "/dist"),  //打包后的文件存放的地方
filename: "bundle.js"   //打包后输出文件的文件名
},
devServer: {
contentBase: "./dist",  // 本地服务器所加载文件的目录
port: "8088",   // 设置端口号为8088
inline: true, // 文件修改后实时更新
historyApiFallback: true    // 不跳转
},
devtool: 'source-map',   // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正则匹配以.css结尾的文件
use: ['style-loader', 'css-loader'] // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
},
{
test: /\.less$/,    // 正则匹配以.less结尾的文件
use: ['style-loader', 'css-loader', 'less-loader']  //需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
},
{
test: /\.(scss|sass)$/, //正则匹配以.scss和.sass结尾的文件
use: ['style-loader', 'css-loader', 'sass-loader']  //需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
},
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_moudles/
}
]
}
}

 现在我们已经可以支持ES6及JSX的语法了,我们用react来试试,但使用react还得先安装两个模块 react 和 react-dom 。

[code]npm install react react-dom --save-dev

接下来我们把hello.js文件修改一下:

[code]// hello.js
import React, {Component} from 'react'; // 这两个模块必须引入

let name = 'Alan';

export default class Hello extends Component {
render() {
return (
<div>
{name}
</div>
);
}
}

// module.exports = function() {
//     let hello = document.createElement('div');
//     hello.innerHTML = "Long time no see!";
//     return hello;
// };

 index.js文件也修改一下:

[code]// index.js
import './css/style.css';   // 导入css
// import './css/blue.scss';   // 导入scss

import React from 'react';
import {render} from 'react-dom';
import Hello from './hello';    //可省略.js后缀名

render(<Hello />, document.getElementById('root'));

// const hello = require('./hello.js');
// document.querySelector("#root").appendChild(hello());

此时运行 npm run dev 后你可能会发现如下结果: 

 这是因为官方默认 babel-loader | babel 对应的版本需要一致,即 babel-loader 需要搭配最新版本 babel。

这里有两种解决方案:

  • 回退低版本
[code]npm install babel-loader@7 babel-core babel-preset-env --save-dev
  • 更新到最高版本
[code]npm install babel-loader @babel/core @babel/preset-env webpack --save-dev

建议采用第一种解决方案,回退后,再次运行 npm run dev ,即可得到结果。

(2)优化babel配置

接下来我们优化一下babel的配置,虽然babel完全可以在webpack.config.js中进行配置,但若之后babel膨胀了,增加了更多的配置项,就会造成冗余,所以我们要将进行模块化。

Babel配置文件的扩展名是.babelrc,它的基本格式如下:

[code]{
"presets": [],
"plugins": []
}

 presets用于指定转码规则,plugins用于指定加载的插件。

常用的转码规则如下:

  • ES 2015:又称为ES 6,是JavaScript语言经过ECMA认证的一个版本的别称,发布与2015年。
  • env:env表示是对当前环境的预处理,而不是像以前使用的babel-preset-es2015只能针对某个环境。
  • react:Facebook开发的一款用于构建用户界面的JavaScript库。
  • stage-0:是对ES 7一些提案的支持,包含下面stage-1、stage-2、stage-3的所有功能以及其他2个插件。
  • stage-1:包含下面stage-2和stage-3的所有功能以及其他4个插件。
  • stage-2:包含stage-3的所有功能以及其他2个插件。
  • stage-3:是ES 7的基本转码规则,包含 transform-async-to-generator 和 transform-exponentiation-operator 2个插件。

presets 指定的是预设的插件集,plugins指定的是单独的插件。例如:

[code]{
"presets": ["es2015", "stage-0", "react"],
"plugins": ["transform-runtime"]
}

 transform-runtime 插件用于运行时编译ES 6。

我们将babel的配置从webpack.config.js中提取出来,将其放到根目录下的.babelrc文件中(webpack会自动调用.babelrc里的babel配置选项)。

在项目根目录下新建.babelrc文件:

[code]// webpack.config.js
const path = require('path');
module.exports = {
entry: path.join(__dirname, "/src/index.js"), // 入口文件
output: {
path: path.join(__dirname, "/dist"),  //打包后的文件存放的地方
filename: "bundle.js"   //打包后输出文件的文件名
},
devServer: {
contentBase: "./dist",  // 本地服务器所加载文件的目录
port: "8088",   // 设置端口号为8088
inline: true, // 文件修改后实时更新
historyApiFallback: true    // 不跳转
},
devtool: 'source-map',   // 会生成对于调试的完整的.map文件,但同时也会减慢打包速度
module: {
rules: [
{
test: /\.css$/, // 正则匹配以.css结尾的文件
use: ['style-loader', 'css-loader'] // 需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
},
{
test: /\.less$/,    // 正则匹配以.less结尾的文件
use: ['style-loader', 'css-loader', 'less-loader']  //需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
},
{
test: /\.(scss|sass)$/, //正则匹配以.scss和.sass结尾的文件
use: ['style-loader', 'css-loader', 'sass-loader']  //需要用的loader,一定是这个顺序,因为调用loader是从右往左编译的
},
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader"
},
exclude: /node_moudles/
}
]
}
}

.babelrc文件(.babelrc文件不能添加注释)

[code]{
"presets": ["env", "react"]
}

 

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