前端工程化基础-vue

时间:2023-03-10 01:37:02
前端工程化基础-vue

由浅入深支持更多功能

1.安装最新版本的node.js和NPM,并了解NPM基本用法。

2.创建一个目录demo。使用npm 初始化配置: npm init  ,执行后会有一系列选项,可按回车快速确认,在demo中生成一个package.json文件。

3.局部安装webpack  :  npm install webpack --save-dev

--save-dev 会作为开发依赖来安装webpack。安装成功后,在package.json中会多一项配置

"devDependencies": {
"webpack": "4.6.0"
}

4.接着需要安装webpack-dev-server,它可以在开发环境中提供很多服务,比如启动一个服务器,热更新,接口代理等

局部安装: npm install webpack-dev-server --save-dev

5.在demo下创建一个js文件:webpack.config.js初始化内容:

var config = {

};

module.exports = config;

6.在package.json的script里增加一个快速启动webpack-dev-server服务的脚本:

{
//...
"scripts":{
"test":*****,
"dev":"webpack-dev-server --open --config webpack.config.js"
} }

当运行npm run dev就会执行webpack-dev-server --open --config webpack.config.js命令。其中--config是指向webpack-dev-server 读取的配置文件路径,这里直接读取我们在上一步创建的webpack.config.js文件。--open会在执行命令是打开浏览器页面,默认地址是127.0.0.1:8080,不过ip和端口都可以配置。

"dev":"webpack-dev-server --host 172.172,172.1 --port 8888 --open --config webpack.config.js"

一般用默认的本机地址就可以。

7.在demo目录下新建一个空的main.js作为入口的文件,然后在webpack.config.js中进行入口的输出和配置:

var path = require('path');
var config = {
entry: {
main: './main'
},
output: {
path: path.join(__dirname, '/dist'),
publicPath: './dist',
filename: 'main.js'
}
}; module.exports = config;

entry中的main就是我们配置的单入口,webpack会从main.js文件开始工作。output中path选项用来存放打包后文件的输出目录,必填项。publicPath是资源文件引用目录,如果在cdn上,这里可以填写cdn地址。filename用于指定输出文件的名称。因此,这里配置的output意为打包后的文件会存放在demo/dist/main.js,只在html引入就可以了。

<body>
<div id="app">Hello world</div>
<script src="/dist/main.js"></script>
</body>

8.运行 npm run dev就能看到hello world字样。

9.逐步完善配置文件

在webpack的世界中,每个文件都是一个模块,比如.css、.js、.html、.jpg、.less等。对于不同的模块需要用不同的加载器来处理,而加载器就是webpack最重要的功能。

安装style-loader和css-loader来处理css样式。

通过npm 来安装:

npm install css-loader --save-dev

npm install style-loader --save-dev

安装完成后再webpack.config.js文件里面配置Loader,增加对.css文件的处理。

var config = {
//.....
module:{
rules:[
{
test:/\.css$/,
use:[
'style-loader',
'css-loader'
]
}
]
}
};

module对象 的rules属性中可以指定一系列的loader,每一个loader都必须包含test和use两个选项。这段的意思是说,当webpack编译过程中遇到require()或import语句导入一个后缀名为.css的文件时,先将它通过css-loader转换,再通过style-loader转换,然后继续打包。use选项的值可以是数组或字符串,如果是数组,它的编译顺序就是从后往前。

在demo下新建一个style.css文件,并在main.js中导入:

/**style.css**/

#app{
  font-size:24px;

  color:#f50;
}

//main.js

import './style.css'

重新执行npm run dev可以看到页面文字变成了红色,字号变大。

下面是执行后的html源码:

前端工程化基础-vue

可以看到,css是通过javascript动态创建style标签来写入的,这意味着样式代码都已经编译在了mian.js文件里,但是在实际业务中可能并不希望这样做,因为项目大了样式会很多,都放在js里太占体积。这时 就要用到webbpack的插件。

webpack的插件功能很强大而且可以定制。这里我们使用一个extract-text-webpack-plugin的插件来把散落在各地的css提取出来,并生成一个main.css的文件,并最终在index.html里通过link的形式加载它。

10.通过npm 安装extract-text-webpack-plugin插件:

npm install extract-text-webpack-plugin --save-dev

然后在配置文件中导入插件,并改写loader配置:

var path = require('path');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var config = {
//.....
module:{
rules:[
{
test:/\.css$/,
use:ExtractTextPlugin.extract({
use: 'css-loader',
fallback: 'style.loader'
})
}
]
},
plugin: [
//重命名提取后的css文件
new ExtractTextPlugin("main.css")
]
}; module.exports = config;

这个时候运行可能会出现“DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead”类似的报错,这个是webpack版本问题

使用 sudo npm install extract-text-webpack-plugin@next  安装4.0就可解决这个问题。

11.单文件组件和vue-loader

在使用webpack构建Vue项目时,可以使用一种新的构建模式:.vue单文件组件。在webpack中使用vue-loader就可以对.vue格式的文件进行处理。

一个.vue文件一般包含3部分,<template>、<script>、<style>

style标签使用scoped属性,表示当前的css只在这个组件有效,如果不加,那么样式会应用到整个项目。style还可以结合css预编译一起使用。比如less处理就可以写成<style lang="less">。使用.vue文件需要安装 vue-loader,vue-style-loader等加载器并做配置。因为要使用es6语法,还需安装babel和babel-loader等加载器。使用npm 逐个安装以下依赖:

npm install --save vue

npm install --save-dev vue-loader

npm install --save-dev vue-style-loader

npm install --save-dev vue-template-compiler

npm install --save-dev vue-hot-reload-api

npm install --save-dev babel

npm install --save-dev babel-loader

npm install --save-dev babel-core

npm install --save-dev babel-plugin-transform-runtime

npm install --save-dev babel-preset-es2015

npm install --save-dev babel-runtime

12.安装完成后,在webpack.config.js配置以支持.vue文件和es6的解析。

var path = require('path');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var config = {
entry: {
main:'./main'
},
output: {
path: path.join(__dirname, '/dist'),
publicPath: '/dist',
filename: 'main.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders :{
css: ExtractTextPlugin.extract({
use: 'css-loader',
fallback: 'vue-style-loader'
})
}
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: 'css-loader',
fallback: 'style-loader'
})
}
]
},
plugins: [
new ExtractTextPlugin("main.css")
]
}; module.exports = config;

vue-loader在编译.vue文件时,会对<template>、<script>、<style>分别处理,所以在vue-loader选项里多了一项options来进一步对不同语言进行配置。比如在对css进行处理时,会先通过css-loader解析,然后把处理结果再交给vue-style-loader处理。当技术栈多样化时,可以给<template>、<script>、<style>都指定不同的语言,比如<template lang="jade">和<style lang="less">,然后配置loader就可以了。

13.在demo目录下新建一个名为.babelrc的文件,并写入babel的配置,webpack会依赖此配置文件来使用babel编译es6代码:

{
"presets": ["es2015"],
"plugins": ["transform-runtime"],
"comments": false
}

配置好这些后就可以使用.vue文件了,每个.vue文件就代表一个组件,组件之间可以相互依赖。

14.在demo目录下新建一个app.vue的文件并写入以下内容:

<template>
<div>Hello {{name}}</div>
</template> <script>
export default {
data() {
return {
name: "vue.js"
}
}
}
</script> <style scoped>
div{
color: #f60;
font-size: 24px;
}
</style>

.vue的组件时没有名称的,在父组件使用时可以对它自定义。写好了组件,就可以在入口main.js中是用它了。打开main.js,把内容替换下面代码:

import Vue from 'vue';
//导入app.vue组件
import App from './app.vue'; //创建Vue实例
new Vue({
el: "#app",
render: h => h(App)
});

render:h => h(App)是es6的写法,等同于

render:function(h){

  return h(App)

}

执行npm run dev,这样第一个vue工程项目就跑起来了。

bug:这个地方vue-loader升级到15版本以上不兼容。需要退回14版本才兼容。

前端工程化基础-vue

这里之所以多了一串data-v-xxx内容,是因为使用了<style scope>功能,如果去掉scope就没有了。

15.在demo 目录下新建两个文件,title.vue和button.vue

title.vue

<template>
<h1>
<a :href="'#' + title">{{title}}</a>
</h1>
</template> <script>
export default {
props:{
title:{
type:String
}
}
}
</script> <style scoped>
h1 a{
color: #3399ff;
font-size: 24px;
}
</style>

button.vue

<template>
<button @click="handleClick" :style="styles">
<slot></slot>
</button>
</template> <script>
export default {
props: {
color: {
type: String,
default: '#00cc66'
}
},
computed:{
styles () {
return {
background: this.color
}
}
},
methods:{
handleClick:function(e){
this.$emit('click',e);
}
}
}
</script> <style scoped>
button{
border: 0;
outline: none;
color: #fff;
padding: 4px 8px;
}
button:active{
position: relative;
top: 1px;
left: 1px;
} </style>

然后改写根实例app.vue组件,把title.vue和button.vue导入:

<template>
<div>
<v-title title="Vue组件化"></v-title>
<v-button @click="handleClick">点击按钮</v-button>
</div>
</template> <script>
//导入组件
import vTitle from './title.vue';
import vButton from './button.vue'; export default {
components: {
vTitle,
vButton
},
methods:{
handleClick:function(e){
console.log(e)
}
} }
</script> <style scoped>
div {
color: #f60;
font-size: 24px;
}
</style>

其中components: {

  vTitle,

  vButton

}

写法是es6写法,等同于:

components: {

  vTitle : vTitle,

  vButton : vButton

}

导入的组件都是局部注册的,而且可以自定义名称,其他用法和组件用法一致。

用于生产环境

我们先对webpack进一步配置,来支持更多的常用功能。

安装 url-loader 和 file-loader来支持图片,字体等文件:

npm install --save-dev  url-loader

npm install --save-dev file-loader

//webpack.config.js

            {
test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
loader: 'url-loader?limit=1024'
}

当遇到.gif、.png、.ttf等格式文件时,url-loader会把它们一起编译到dist目录下,"limit=1024"表示如果这个文件小于1kb,就以base64的形式加载,不会生产一个文件。

找一张图片,保存为demo/images/image.png,并在app.vue中加载它:

<template>
<div>
<v-title title="Vue组件化"></v-title>
<v-button @click="handleClick">点击按钮</v-button>
<p>
<img src="./images/image.png" style="width: 300px">
</p>
</div>
</template>

webpack打包后的产物:

单页面富应用(SPA)技术,意味着最终只有一个html文件,其余都是静态资源。实际部署到生产环境,一般都会将html挂载后端程序下,由后端路由渲染这个页面,将所有的静态资源(css、js、image、iconfont等)单独部署到CDN,当然也可以和后端程序部署在一起,这样就实现了前后端完全分离。

在webpack的output选项里指定了path和publicPath,打完包后,所有的资源都会保存在demo/dist目录下。

打包会用到下面两个依赖,使用NPM安装:

npm install --save-dev webpack-merge

npm install --save-dev html-webpack-plugin

为了方便开发和生产环境的切换,我们在demo目录下再新建一个用于生产环境的配置文件  webpack.prod.config.js

编译打包,直接执行webpack命令就可以。在package.json中,再加入一个build的快捷脚本用来打包:

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server --open --config webpack.config.js",
"build": "webpack --progress --hide-modules --config webpack.prod.config.js"
},

webpack.prod.config.js的代码如下:

var webpack = require('webpack');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var merge = require('webpack-merge');
var webpackBaseConfig = require('./webpack.config.js'); //清空基本配置的插件列表
webpackBaseConfig.plugins = []; module.exports = merge(webpackBaseConfig, {
output: {
publicPath: '/dist/',
//将入口文件重命名为带有20位hash值的唯一文件
filename: '[name].[hash].js'
},
plugins: [
new ExtractTextPlugin({
//提取css,并重命名为带有20位hash值得唯一文件
filename: '[name].[hash].css',
allChunks: true
}),
//定义当前node环境为生产环境
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
//压缩js
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
//提取模板,并保存入口html文件
new HtmlwebpackPlugin({
filename: '../index_prod.html',
template: './index.ejs',
inject: false
}) ]
});

上面安装的webpack-merge模块就是用于合并两个webpack的配置文件,所以prod的配置是在webpack.config.js基础上扩展的。静态资源在大部分场景下都有缓存,更新上线后一般都希望用户能及时地看到内容,所以给打包后的css和js文件都加了20位的hash值,这样文件名就唯一了,只要不对html文件设置缓存,上线后立即就可以加载最新的静态资源。

html-webpack-plugin是用来生成html文件的,它通过template选项累读取指定的模板index.ejs,然后输出到filename指定的目录,也就是说demo/index_prod.html,模板index.ejs动态设置了静态资源的路径和文件名。

最后在终端运行npm run build,等一会就会打完包,成功后会在demo目录下生成一个dist目录,里面就是打完包所有的静态资源。

bug:webpack升级4以上,打包会报错:

Error: webpack.optimize.UglifyJsPlugin has been removed, please use config.optimization.minimize instead.

这是因为新版本把方法移除了。解决办法1:在script里面配置

"build": "webpack --mode production"

就会自动打包。

解决办法2:webpack内置的JS压缩插件不能使用了,可以安装uglifyjs-webpack-plugin插件,使用同其他非内置插件。