九.组件化高级
1.插槽slot
- 作用:让封装的组件更加具有扩展性,使用者可以决定组件内部的一些展示内容。
1.1 插槽的基本使用
- 可设置默认值button
- 如果有多个值,同时放入到组件进行替换时,会将其一起作为替换元素。
<div id="app">
<cpn></cpn>
<cpn><span>哈哈哈</span></cpn>
<cpn><i>hehehe</i></cpn>
</div>
<template id="cpn">
<div>
<h2>我是组件</h2>
<p>-----</p>
<!-- 插槽,预留位置,可设置默认值 -->
<!-- <slot></slot> -->
<slot><button>按钮</button></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
},
components: {
cpn: {
template: '#cpn',
}
}
})
</script>
1.2 具名插槽的使用
- 给slot元素一个name属性即可
<div id="app">
<!-- 仅覆盖某一个插槽 -->
<cpn><span slot="center">标题</span></cpn>
</div>
<template id="cpn">
<div>
<!-- 设置name可控制某一个插槽 -->
<slot name="left"><span>左</span></slot>
<slot name="center"><span>中</span></slot>
<slot name="right"><span>右</span></slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
},
components: {
cpn: {
template: '#cpn',
}
}
})
</script>
1.3 编译作用域
- 父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在自己作用域内编译。
<div id="app">
<!-- 此时isShow为true,作用域为Vue实例内,可渲染 -->
<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
<div>
<h2>我是子组件</h2>
<p>hahhahah</p>
<!-- 此时isShow为false,作用域为该组件内,不可渲染 -->
<button v-show="isShow"></button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
isShow: true
},
components: {
cpn: {
template: '#cpn'
},
data() {
return {
isShow: false
}
}
}
})
</script>
1.4 作用域插槽
- 父组件替换插槽的标签,但是内容由子组件来提供。
<div id="app">
<cpn></cpn>
<!-- 实现效果:中间用-分割 -->
<cpn>
<!-- 此时拿不到pLanguages中的数据(作用域,父组件拿不到子组件中的数据) -->
<!-- <span v-for="item in pLanguages"></span> -->
<!-- 目的:获取子组件中的pLanguages -->
<template slot-scope="slot">
<!-- <span v-for="item in slot.data">{{item}} - </span> -->
<span>{{slot.abc.join(' - ' )}}</span>
</template>
</cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<!-- abc可由自己命名 -->
<slot :abc="pLanguages">
<ul>
<li v-for="item in pLanguages">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message : '你好啊',
},
components: {
cpn: {
template: '#cpn',
data() {
return {
pLanguages: ['JavaScript','c++','Java', 'Python', 'Go']
}
}
}
}
})
</script>
十.模块化开发
1.为什么要有模块化?
- 不同文件命名相互冲突 => 解决办法: 匿名函数(闭包) => 坏处:代码复用性降低,之前定义的数据及函数无法使用 => 使用模块作为出口 (在匿名函数内部定义一个对象,给它添加各种需要暴露到外面的属性和方法,最后返回这个对象,并且在外面使用ModuleA接收,每个人只需要使用属于自己模块的属性和方法即可)
2. CommonJS(了解)
- 模块化有两个核心: 导出和导入
- CommonJS的导出(export):
module.exports = {
flag : true,
test(a, b) {
return a + b
},
demo(a, b) {
return a * b
}
}
- CommonJS的导入(require):
let { test, demo, flag } = require('moduleA');
3. ES6的模块化实现
<-- index.html -->
<body>
<script src="aaa.js" type="module"></script>
<script src="bbb.js" type="module"></script>
<script src="ccc.js" type="module"></script>
</body>
//aaa.js
var name = '小明'
var age = 18
var flag = true
function sum(num1, num2) {
return num1 + num2
}
if (flag) {
console.log(sum(20,30))
}
//1.导出方式一(先定义再导出):
export {
flag, sum
}
//2.导出方式二(边定义边导出):
export var num1 = 1000;
export var height = 1.88;
//3.导出函数/类
export function mul(num1, num2) {
return num1 * num2
}
export class Person {
run() {
console.log('在奔跑');
}
}
//4.导出默认值
const address = '上海'
export default address
//bbb.js
import {sum} from "./aaa.js"
var name = '小红'
var flag = false
console.log(sum(100,200))
//ccc.js
//1.导入export{}中定义的变量
import {flag, sum} from "./aaa.js";
if (flag) {
console.log('小明是天才,哈哈哈');
console.log(sum(23,34));
}
//2.直接导入export定义的变量
import { num1,height } from "./aaa.js";
console.log(num1);
console.log(height);
//3.导入 export的function/class
import {mul,Person} from "./aaa.js";
console.log(mul(45,56));
const p = new Person();
p.run();
//4.导入的是默认值,同一模块内不允许存在多个(导入者可以自己来命名)
import addr from "./aaa.js";
console.log(addr);
//5.统一全部导入
import * as aaa from "./aaa.js";
console.log(aaa.flag);
十一. webpack
1. webpack 相关介绍
1. 1 webpack模块化
- webpack 其中一个核心就是让我们能进行模块化开发,并且帮助我们处理模块之间的依赖关系。不仅仅是JavaScript文件,我们的CSS,图片,json 文件等在 webpack 中都可以当成模块来使用。
1.2 webpack的打包
1.2.1 含义
- 将 webpack中的各种资源模块进行打包,合并成一个或多个包(Bundle)。并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将scss转成css,将ES6语法转成ES5语法,将TypeScript转成JavaScript等操作。
1.2.2 和 grunt/gulp的对比
- grunt/gulp的核心是Task,我们可以通过配置一系列的task,并且定义task要处理的事务(例如ES6, ts转化,图片压缩等),之后让grunt/gulp来依次执行这些task,而且让整个流程自动化,所以grunt/gulp也被称为前端自动化任务管理工具。
- 如果工程模块依赖非常简单,甚至没有用到模块化的概念,或者只需要进行简单的合并,压缩,使用grunt/gulp即可。如果整个项目使用了模块化管理,而且相互依赖非常强,就可以使用webpack。
- grunt/gulp更加强调的是前端流程的自动化,模块化并不是它的核心。而webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能是其附加功能。
2. webpack安装及基本使用
2.1 安装
- 安装 webpack 前首先要安装 Node.js,它自带了软件包管理工具npm
- 查看自己的node版本,在终端输入node -v 即可查看
- 全局安装 webpack(这里我们先指定版本号3.6.0,因为 vue cli2依赖该版本),输入 npm install webpack@3.6.0 -g
- 局部安装 webpack(后续才会使用,–save-dev是开发时依赖,项目打包后不需要继续使用),先 cd 对应目录,再输入 npm install webpack@3.6.0 --save-dev
- 在终端直接执行webpack命令,使用的是全局安装webpack
- 当在package.json中定义了scripts时,其中包含了webpack命令,使用的则是局部webpack
- 安装完成后可通过 webpack --version 查看版本号
2.2 准备工作
2.2.1 创建相关文件夹
2.2.2 文件和文件夹解析
- dist文件夹:用于存放之后打包的文件
- src文件夹:用于存放我们写的源文件
- main.js : 项目的入口文件
- mathUtils.js : 定义了一些数学工具函数,可以在其他地方应用并且使用。
- index.html : 浏览器打开时展示的首页html
- package.json : 通过 npm init 生成的,npm包管理的文件
//mathUtils.js
function add(num1, num2) {
return num1 + num2
}
function mul(num1, num2) {
return num1 * num2
}
//CommonJS规范,直接引用该文件时浏览器不支持
module.exports = {
add,
mul
}
//main.js
const { add, mul} = require('./mathUtils.js')
// const math = require('./mathUtils')
// console.log(math.add(10,20));
console.log(add(10,20));
console.log(mul(10,20))
2.2.3 使用webpack打包
- 步骤:
- cd 对应文件夹,如: cd 01-webpack的起步
- 将src文件夹中写好的源文件main.js打包至dist文件夹中,打包后生成文件为bundle.js,输入 webpack ./src/main.js ./dist/bundle.js
- 注意:
- 首次使用webpack打包时,报错:webpack : 无法加载文件 C:\Users\XXX\AppData\Roaming\npm\webpack.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
- 原因:powershell对于脚本的执行有着严格的安全限制 ,默认是不载入配置文件的, 因而报错,可使用Set-ExecutionPolicy指令来修改PowerShell中执行策略的用户首选项。
- 解决方式:①以管理员权限打开PowerShell;②输入Set-ExecutionPolicy RemoteSigned命令,将脚本执行权限修改为RemoteSigned;③输入Y以确认
- 拓展:共有四个权限等级可选择:
Restricted | 不载入配置文件 不执行脚本【默认该权限等级】 |
AllSigned | 所有配置文件和脚本必须通过信任的出版商签名 这里的脚本页包括在本地计算机上创建的脚本 |
RemoteSigned | 所有从互联网上下载的脚本必须通过信任的出版商签名 |
Unrestricted | 载入所有的配置文件和脚本,如果运行了一个从互联网上下载且没有数字签名的脚本 在执行前会提示是否执行 |
3. webpack的相关配置
3.1 webpack.config.js
- 实现:只要在相应文件终端中输入webpack,即可将文件打包好
- 需要用到node相关包(如path)时,在终端中输入 npm init,完成后会生成一个package.json文件
- 安装相关依赖,输入npm install
//webpack.config.js
const path = require('path')
module.exports = {
//入口
entry : './src/main.js',
//出口
output : {
// 只能写绝对路径,此时需要动态获取
path : path.resolve(__dirname, "dist"),
filename : 'bundle.js',
},
}
3.2 package.json
- 实现:相关命令的映射,在相应文件终端中输入 npm run bulid,即可实现对应命令webpack。
- 方法:在 package.json文件的脚本“scripts”中添加一行: “build”: “webpack”
- 注意:package.json中的scripts脚本在执行时,会按照一定顺序寻找命令对应位置。
- 首先,会寻找本地的node_modules/.bin路径中对应的命令,如果没有找到,才会到全局的环境变量中寻找。
3.3 局部安装webpack
- 在终端中使用的webpack往往是全局的,而每个项目依赖特定的webpack版本,可能与全局的版本不一致,从而导致打包出现问题。所以,通常一个项目都有自己局部的webpack。
- 步骤:
-
- 项目中安装自己的局部webpack: npm install webpack@3.6.0 --save-dev
-
- 启动webpack打包: node_modules/.bin/webpack
-
- 安装完成后会生成一个node_modules文件夹
4. webpack使用相关文件
- 大部分loader都可以在 webpack官网中找到,并且学习对应用法
- 步骤一:通过 npm 安装需要使用的 loader
- 步骤二: 在 webpack.config.js中的module关键字下去进行配置
4.1 webpack中使用css文件的配置
4.1.1 给webpack扩展对应的 loader(css-loader, style-loader)
- 注意: 由于我们之前安装的webpack版本是3.6.0,此时安装的css loader或style loader由于版本太高会报错。故此时我们用的是
- npm install --save-dev css-loader@2.0.2
- npm install style-loader@0.23.1 --save-dev
module: {
rules: [
{
// 正则表达式,\表示转义,$表示结束,匹配以.css结尾的文件使用以下loader
test: /\.css$/,
// css-loader只负责将css文件进行加载
// style-loader负责将样式添加到DOM中
// 使用多个loader时,从右向左读取
use: [ 'style-loader','css-loader' ]
}
]
}
4.1.2 遇到的小bug:
ERROR in ./src/main.js
Module not found: Error: Can't resolve './info.js' in 'D:\Desktop\前端\learnVueJs\06-webpack的使用\03-webpack的loader\src'
解析:由报错信息知,系统无法找到资源,一般这种bug无非两种情况,一是路径写错了,二是资源不存在。查阅自己原码,资源存在,路径正确,那么到底是哪里出了问题,找了半天,终于发现,在引入资源的时候,单引号写成了双引号
- 正确引入:
- import * as info from './js/info'
4.2 webpack中对less文件的处理
- 先给webpack 安装对应的 loader(less-loader)及less
- 这里我们安装的版本
npm install --save-dev less-loader@4.1.0 less@3.9.0
//special.less
@fontSize: 50px;
@fontColor: orange;
body {
font-size: @fontSize;
color: @fontColor;
}
- 再在 webpack.config.js中的module关键字下去进行配置
- 再在main.js中设置依赖,才会将它进行打包
//4.依赖less文件
require('./css/special.less')
document.writeln('<h2>你好啊,张万森!</h2>')
4.3 webpack中对图片文件的处理
- 引入图片需要用到url,故给webpack 安装 url-loader
npm install --save-dev url-loader@1.1.2
- 在 webpack.config.js中的module关键字中的rules进行配置
//url-loader的配置
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
// 当加载的图片小于limit时,会将图片编译成base64字符串形式
// 当加载的图片大于limit时,需要使用file-loader模块进行加载
limit: 54000
}
}
]
},
- 给webpack 安装 file-loader
npm install --save-dev file-loader@3.0.1
- 图片处理-修改文件名称(在module的options内设置)
name: 'img/[name].[hash:8].[ext]'
4.4 webpack中对ES6语法的处理
- webpack打包好的js文件中,写的ES6语法并没有转成ES5,意味着可能一些对ES6不支持的浏览器无法很好的运行我们的代码
- 之前说过,想要将ES6语法转成ES5,需要使用babel
- 而在webpack中,直接使用babel对应的loader即可
4.4.1 安装对应loader(babel-loader)
npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
4.4.2 配置 webpack.config.js文件
//babel-loader的配置
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
- 重新打包,查看bundle.js文件,发现其中的内容变成了ES5的语法
4.5 webpack中使用Vue的配置
4.5.1 引入vue.js
-
- 安装
npm install vue --save
npm install vue@2.5.21 --save
- 安装
//main.js
//5.使用Vue进行开发
import Vue from 'vue'
new Vue({
el: '#app',
data: {
message: 'Hello Webpack'
}
})
<body>
<div id="app">
<h2>{{message}}</h2>
</div>
<script src="./dist/bundle.js"></script>
</body>
-
- 报错,无法解析vue代码
- vue在构建最终发布版本时,构建了两类版本
runtime-only | 代码中,不可以有任何的template |
runtime-compiler | 代码中,可以有template,因为compiler可以用来编译template |
-
- 解决方案
- 修改 webpack.config.js 中的相关配置,在module.exports中添加以下代码,从而使用不同版本
resolve: {
//alias:别名
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
4.5.2 创建Vue时template和el的关系
- 写好的html模板在之后的开发中,不希望被频繁修改
- 解决方法:定义template属性
- 当Vue中既有el又有template时,解析时会用template的模板替换index.html中挂载的 内模块
//main.js
new Vue({
el: '#app',
template: `
<div>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
`,
data: {
message: 'Hello,张万森',
name: '林北星',
},
methods: {
btnClick() {
}
}
})
4.5.3 Vue的终极使用方案(Vue组件化开发引入)
- 递进方案
- 为防止Vue中内容繁琐,我们将其封装成App组件,新建一个app.js 文件,将Vue中的主要内容写在app.js 文件中。在main.js中导入该组件即可。
//main.js
//5.使用Vue进行开发
import Vue from 'vue'
import App from './vue/app'
new Vue({
el: '#app',
template: `<App/>`,
components: {
App
}
})
//app.js
export default{
template: `
<div>
<h2>{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
`,
data() {
return {
message: 'HelloWebpack',
name: '林北星',
}
},
methods: {
btnClick() {
}
}
}
- 最终方案(分离化)
- 将template,script,style全部分离
- 新建一个App.vue文件,最后在main.js中导入(import App from ‘./vue/App.vue’)
<template>
<div>
<h2 class="title">{{message}}</h2>
<button @click="btnClick">按钮</button>
<h2>{{name}}</h2>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
message: 'HelloWebpack',
name: '林北星',
}
},
methods: {
btnClick() {
}
},
}
</script>
<style scoped>
.title {
color: green;
}
</style>
- 此时,这个特殊的文件及特殊格式并不能被正确的加载,需要 vue-loader 及 vue-template-compiler 来进行处理。
- 安装 vue-loader 及 vue-template-compiler
npm install vue-loader vue-template-compiler --save-dev
npm install --save-dev vue-loader@13.0.0 vue-template-compiler@2.5.21
- 修改webpack.config.js配置文件
- 安装 vue-loader 及 vue-template-compiler
//vue-loader的配置
{
test:/\.vue$/,
use: ['vue-loader']
}
- 注意: 文件路径如果想省略后缀名,可在webpack.config.js文件中module.exports的resolve内添加
extensions: ['.js', '.css', '.vue'],
5. plugin的使用
5.1 认识plugin
-
plugin是什么?
plugin是插件的意思,通常是用于对某个现有框架进行扩展,webpack中插件,就是对webpack现有功能的各种扩展,如打包优化,文件压缩等。
-
loader和plugin的区别
loader主要用于转换某些类型的模块,是一个转换器;
plugin是插件,是对webpack本身的扩展,是一个扩展器。 -
plugin的使用过程
①通过npm 安装 需要使用的plugins(某些webpack已经内置的插件不需要安装);
②在webpack.config.js中的plugins中 配置插件。
5.2 添加版权的plugin
-
(BannerPlugin,属于webpack自带插件)
-
修改webpack.config.js配置文件
-
重新打包程序(npm run build),查看bundle.js文件的头部,会看到添加的版权信息
//webpack.config.js
//导入时会自动去node_modules中寻找
const webpack = require('webpack')
module.exports = {
...
plugins: {
new webpack.BannerPlugin('最终解释权归橙点点所有')
}
}
5.3 打包html的plugin
-
真实发布项目时,发布的是dist文件夹中的内容,故我们需要将index.html文件打包到dist文件夹中,这时就需要使用HtmlWebpackPlugin插件
-
该插件会自动生成一个index.html文件(可指定模板),并将打包到js文件自动通过script标签插入到body中
-
安装HtmlWebpackPlugin插件
npm install html-webpack-plugin --save-dev
npm install html-webpack-plugin@3.2.0 --save-dev
-
修改plugins相关配置
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: {
new webpack.BannerPlugin('最终解释权归橙点点所有'),
new HtmlWebpackPlugin({
template: 'index.html'
//表示根据什么模板生成index.html,此时需要删除之前在output中添加的publicPath属性,否则插入的script标签中的src可能会出问题
}),
}
}
5.4 js压缩的plugin
-
在项目发布前,必然要对js等文件进行压缩处理,使用第三方插件uglifyjs-webpack-plugin,并且指定版本号1.1.1,和CLI2保持一致。
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
-
修改webpack.config.js文件
const uglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
plugins: [
new uglifyJsPlugin()
}
]
-
查看打包后的bundle.js文件,已经被压缩。
-
bug:使用vscode始终报错,打包不成功。(版本不要太高)
webpack用plugin打包html文件时报错npm ERR! Failed at the meetwebpack@1.0.0 build script. npm ERR!..
原因:plugins:[...]
必须使用[ ],不能使用 {}
6. 搭建本地服务器
-
这个本地服务器基于node.js搭建,内部使用express框架,可以实现让浏览器自动刷新来显示修改后的结果。
-
是一个单独模块,在webpack中使用前需要先安装
npm install --save-dev webpack-dev-server@2.9.1
-
webpack.config.js文件配置修改如下:
module.exports = {
...
devServer: {
contentBase: './dist',
inline: true,
}
}
-
注释:
-
可再配置另外一个scripts:
"dev": "webpack-dev-server --open"
- –open参数表示直接自动打开浏览器
7. 配置文件的分离
- 生产配置,开发配置,公共配置(base.config.js)进行分离
- 想要实现配置文件的合并,需要安装webpack–merge
npm install webpack-merge --save-dev
npm install webpack-merge@4.1.5 --save-dev
- 《注意版本号,版本太高会报错》
//prod.config.js
//生产时的配置:base + prod
const uglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin');
const webpackMerge = require('webpack-merge');
const baseConfig = require('./base.config');
module.exports = webpackMerge(baseConfig,{
plugins: [
new uglifyJsWebpackPlugin()
],
})
- 此时webpack.config.js已经被完全分离,可以被删除,但需要修改package.json中的scripts脚本内容
"build": "webpack --config ./build/prod.config.js", "dev": "webpack-dev-server --open --config ./build/dev.config.js"