1. 插槽
1.1 插槽的基本使用
组件的插槽:
组件的插槽也是为了让我们封装的组件更加具有扩展性。
让使用者可以决定组件内部的一些内容到底展示什么。
定义插槽的步骤
在组件中使用一个特殊的元素
<slot></slot>
可以为组件开启一个插槽。该插槽插入什么内容取决于父组件如何使用。
<div id="app">
<cpn><span>用户</span></cpn>
<hr>
<cpn></cpn>
<hr>
<cpn>
<span>更多</span>
<span>收藏</span>
</cpn>
</div>
<template id="cpn">
<div>
<span>返回</span>
<h1>我是子组件</h1>
<slot>
<!-- 设置一个默认的span -->
<span>登录</span>
</slot>
</div>
</template>
<script src="../../js/vue.js"></script>
/*
* 1. 插槽的基本使用 <slot></slot>
* 2. 插槽的默认值 <slot></slot>
* 3. 如果有多个值同时放入到插槽中进行替换的时候,一起作为替换元素。
* */
const app = new Vue({
el: '#app',
data: {},
components: {
'cpn': {
template: '#cpn'
}
}
});
1.2 具名插槽
- 定义具名插槽,需要在
slot
标签中使用name
属性标识该插槽的名称,在使用时在具体的标签中使用slot
属性指定需要替换的插槽的名称。
<div id="app">
<cpn>
<span slot="center">我是标题</span>
</cpn>
<hr>
<cpn>
<span slot="left">返回</span>
</cpn>
<hr>
<cpn>
<span slot="left">返回</span>
<span slot="center">我是标题</span>
<span slot="right">用户</span>
</cpn>
</div>
<template id="cpn">
<div>
<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>
const app = new Vue({
el: '#app',
data: {},
components: {
'cpn': {
template: '#cpn'
}
}
});
1.3 编译作用域
<div id="app">
<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
<div>
<h1>我是子组件</h1>
<span>6666</span>
<!-- 这里的isShow是子组件中的所以下面是不会显示的 -->
<span v-show="isShow">我会显示吗?</span>
</div>
</template>
<script src="../../js/vue.js"></script>
const app = new Vue({
el: '#app',
data: {
isShow: true
},
components: {
'cpn': {
template: '#cpn',
data() {
return {
isShow: false
}
}
}
}
});
1.4 作用域插槽
- 当子组件中的数据使用了默认的显示方式,但是父组件希望拿到子组件的数据在其基础上修改显示方式,就可以使用作用域插槽。
作用域插槽的使用步骤
- 在子组件中定义一个插槽,可以指定一种默认的显示方式。并在插槽上使用
自定义属性
绑定一个子组件的数据,传递给父组件。
- 父组件使用
template
标签 和slot-scope
属性获取来自子组件的数据,在slot-scope
中指定一个属性接收数据。
<div id="app">
<!-- 默认以列表方式显示 -->
<cpn></cpn>
<!-- 这里希望使用分割符方式进行展示,但是如何拿到子组件的数据? -->
<cpn>
<!-- 父组件需要使用 template 获取子组件传递的数据 -->
<template slot-scope="cpnSlot">
<span>{{cpnSlot.data.join(' - ')}}</span>
</template>
</cpn>
<cpn>
<!-- 父组件展示列表时希望使用 * 分割 -->
<template slot-scope="myData">
<span>{{myData.data.join(' * ')}}</span>
</template>
</cpn>
</div>
<template id="cpn">
<div>
<!-- :data="pLanguage" 自定义一个属性将子组件的数据传递给父组件 -->
<slot :data="pLanguage">
<ul>
<!-- 默认是使用列表方式进行展示 -->
<li v-for="item in pLanguage">{{item}}</li>
</ul>
</slot>
</div>
</template>
<script src="../../js/vue.js"></script>
/* 作用域插槽是父组件替换插槽的标签,但是内容由子组件提供 */
/* 问题:如何在父组件中拿到子组件中的数据 */
const app = new Vue({
el: '#app',
data: {},
components: {
cpn: {
template: '#cpn',
data() {
return {
pLanguage: ['JavaScript', 'Java', 'C++', 'C#']
};
}
}
}
});
2. 前端模块化开发
2.1 JavaScript原始功能
存在的问题
客户端需要完成的事情越来越多,代码量也是与日俱增。
为了应对代码量的剧增,我们通常会将代码组织在多个js文件中,进行维护。
但是这种维护方式,依然不能避免一些灾难性的问题。
如下面的代码
index.html
<body>
<script src="../js/xiaoming.js"></script>
<script src="../js/xiaohong.js"></script>
<script src="../js/tiancai.js"></script>
</body>
xiaoming.js
// 小明的文件
var name = '小明';
var age = 23;
function sum(a, b) {
return a + b;
}
var flag = true;
if (flag) {
console.log(sum(12, 36));
}
xiaohong.js
// 小红的js文件
var name = '小红';
console.log(name);
var flag = false;
tiancai.js
if (flag) {
console.log('我真是个天才');
}
- 上面代码由于调用顺序问题,和变量名重名问题,导致后面的代码修改了前面的变量,出现问题。
2.2 解决方案:匿名函数
- 我们可以使用匿名函数来解决方面的重名问题,在
xiaoming.js
文件中。
3. webpack
对打包的理解
理解了webpack可以帮助我们进行模块化,并且处理模块间的各种复杂关系后,打包的概念就非常好理解了。
就是将
webpack
中的各种资源模块进行打包合并成一个或多个包(Bundle
)。并且在打包的过程中,还可以对资源进行处理,比如压缩图片,将
scss
转成css
,将ES6
语法转成ES5
语法,将TypeScript
转成JavaScript
等等操作。
3.1 webpack 的介绍和安装
webpack
要正常运行必须依赖于node
。node
为了可以正常执行很多代码,必须包含各种包。查看自己的
node
版本:
node -v 或者 node --version
- 全局安装
webpack
(这里我先指定版本号3.6.0
,因为vue cli2
依赖该版本)。
npm install webpack@3.6.0 -g
- 局部安装webpack(后续才需要)。
cd 对应目录
npm install webpack@3.6.0 --save-dev
为什么全局安装后,还需要局部安装呢?
在终端直接执行
webpack
命令,使用的全局安装的webpack
。当在
package.json
中定义了scripts
时,其中包含了webpack
命令,那么使用的是局部webpack
。
3.2 webpack 的基本使用
在项目文件夹下创建
dist
和src
文件夹。dist
文件夹用于存放的是打包完成的文件。src
存放的是打包之前的文件。场景 : 创建
main.js
和mathUtils.js
、info.js
三个文件。在项目下创建index.html
文件。下面分别使用CommonJs
模块化 和ES6
模块化。最终使用webpack 打包
。main.js
分别导入info.js
和mathUtils.js
的模块。
/* CommonJS的方式 */
const {add, mul} = require('./mathUtils.js');
console.log(add(20, 30));
console.log(mul(100, 10));
/* 使用ES6的方式 */
import {age, height, name} from './info.js';
console.log(name);
console.log(age);
console.log(height);
/*
webpack 打包命令 :
1. 切换到当前目录 ;
2. 使用 webpack ./src/main.js ./dist/bundle.js 进行打包;
*/
-
info.js
使用ES6
模块化方式。
export const name = '张三';
export const age = 23;
export const height = 1.88;
-
mathUtils.js
使用CommonJs
模块化方式。
function add(a, b) {
return a + b;
}
function mul(a, b) {
return a * b;
}
/* CommonJs的方式 */
module.exports = {
add,
mul
};
- 使用命令进行打包操作:
webpack ./src/main.js ./dist/bundle.js
。
3.3 webpack.config.js配置和package.json配置
webpack.config.js 文件
- 在项目下创建一个
webpack.config.js
的文件,定义打包的对象。
上面文件定义完成之后便可以在项目路径下使用
webpack
命令直接打包了。初始化
package.json
文件配置命令映射。使用npm init
命令初始化package.json
文件。
如上配置之后可以使用
npm run build
打包项目。但是此时使用打包的
webpack
是全局安装的webpack
但是在之后可能全局webpack
版本和项目使用的webpack
版本不一致的情况,此时就需要在项目的目录下自己安装一个本地(局部)webpack
。使用命令npm install webpack@3.6.0 --save-dev
或cnpm install webpack@3.6.0 --save-dev
但是不建议使用cnpm
,我试过可能因为下载太快,我使用webstorm
加载过程中非常卡 。
3.4 什么是loader?
webpack
可以使用 loader 来预处理文件。这允许你打包除JavaScript
之外的任何静态资源。loader
可以将所有类型的文件转换为webpack
能够处理的有效模块,然后你就可以利用webpack
的打包能力,对它们进行处理。本质上,webpack loader
将所有类型的文件,转换为应用程序的依赖图(和最终的bundle
)可以直接引用的模块。在我们之前的实例中,我们主要是用
webpack
来处理我们写的js代码,并且webpack
会自动处理js
之间相关的依赖,但是,在开发中我们不仅仅有基本的js
代码处理,我们也需要加载css
、图片,也包括一些高级的将ES6
转成ES5
代码,将TypeScript
转成ES5
代码,将scss
、less
转成css
,将.jsx
、.vue
文件转成js
文件等等。对于webpack
本身的能力来说,对于这些转化是不支持的。那怎么办呢?给webpack
扩展对应的loader
就可以啦。
3.5 loader使用过程:
步骤一:通过
npm
安装需要使用的loader
。步骤二:在
webpack.config.js
中的modules
关键字下进行配置。大部分
loader
我们都可以在webpack
的中文网中找到。
3.6 使用 css-loader 和 style-loader 的场景
- 编写一个样式文件
noemal.css
文件,在main.js
(入口文件)中进行使用require()
引入。
require('./css/normal.css');
- 此时如果使用
nom run build
打包的话将会出现下面一个问题。
- 查看官方文档,使用命令安装
css-loader
此时未指定版本默认是最新版本。
npm install --save-dev css-loader
- 安装完成之后在
webpack.config.js
中进行配置:
- 配置完成之后,发现尚未可??? 原来是还缺少了一个
loader
style-loader
。style-loader
将模块的导出作为样式添加到DOM
中。安装style-loader
。
npm install style-loader --save-dev
- 安装配置完成还有个问题 ??? ,,,
(node:58300) UnhandledPromiseRejectionWarning: TypeError: this.getResolve is not a function
。 百度一下发现,是我的版本太高了,我按照提示将package.json
下的依赖版本进行修改,重新使用npm install
对依赖进行安装之后。终于可以了。
3.7 webpack 对 less 文件的处理
在开发过程中我们大多数时候会使用到
less
语法,所以webpack
需要提供对less
的支持。安装
less-loader
,但是less-loader
需要less
支持。同时需要安装less
。
npm install --save-dev less-loader less // 这样安装的是最新版的,后面会出问题 可以后面修改版本或者现在修改版本
npm install --save-dev less-loader@4.1.0 less@4.1.0
- 安装完成之后在
webpack.config.js
中配置less-loader
:
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
}
3.8 webpack对图片文件的处理案例
准备两张图片,大小分别一大一小。之后可以设置文件大小一个分界线。
在样式中设置背景图片:该样式文件已经在
main.js
中引入,到时候打包将会将样式文件打包为一个模块。
body {
/*background-color: #f00;*/
background: url(../images/green-logo.png) no-repeat;
background-size: cover;
}
body {
/*background-color: #f00;*/
background: url(../images/green-logo-mini.png) no-repeat;
background-size: cover;
}
- 使用
npm run build
进行打包,发现需要安装适当的loader
。
npm install --save-dev url-loader
- 安装完成之后在
webpack.config.js
文件中进行配置:options: {}
中配置的是打包的一些属性。limit
相当于文件大小的分界线,如果超出该分界线是需要file-loader
的支持的。
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
/* 当图片小于指定的尺寸的时候回将其转换为base64位的图片 */
limit: 10240
}
}
]
}
- 安装
file-loader
,npm install --save-dev file-loader
。
url-loader中options对象的属性 name
-
name
属性:name: 'img/[name].[hash:8].[ext]'
。
img
:文件要打包到的文件夹;
name
:获取图片原来的名字,放在该位置;
hash:8
:为了防止图片名称冲突,依然使用hash
,但是我们只保留8
位;
ext
:使用图片原来的扩展名。
3.9 babel 对 ES6 语法的处理 babel-loader
未使用
babel-loader
转换打包,还存在const
关键字声明变量。
- 使用
babel
将ES6
转为ES5
。
安装
babel-loader
:npm install --save-dev babel-loader@7 babel-core babel-preset-es2015
。在
web.config.js
中配置babel-loader
。exclude
属性用于排除某些文件夹,不对其进行转换。
{
test: /\.js$/,
/* exclude 是排除的意思 */
/* include 是包含的意思 */
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
}
3.10 webpack配置Vue的过程
- 使用命令
npm install --save vue
安装Vue
,在main.js
中导入Vue
。
// 1. 导入 vue
import Vue from 'vue';
- 使用
Vue
输出一段文字。
const app = new Vue({
el: '#app',
data: {
message: 'Hello webpack!'
}
});
- 启动运行时发现出现下面的问题,是因为在
Vue
打包中有很多的版本其中有两个是runtime-only
和runtime-compiler
。
解决:在webpack.config.js文件中module同层级下配置如下代码
resolve:{
alias:{
'vue$':'vue/dist/vue.esm.js'
}
}
Vue打包的两个版本
runtime-only
: 代码中,不可以有任何的template
。runtime-compiler
: 代码中可以有template
,因为compiler
可以用于编译template
。
3.11 使用创建Vue
时 template
和 el
的关系
在前面的
Vue
实例中,我们定义了el
属性,用于和index.html
中的#app
进行绑定,让Vue
实例之后可以管理它其中的内容。上面我们将页面中
<div id="app"></div>
包裹的代码删除,在main.js
中使用template
编写相应的模板进行替换。
3.12 Vue的终极使用方案
使用 JS
方式文件分离组件
- 但是使用这种方式还是未将
template
和js
代码相分离开。
创建 Vue
文件的方式分离组件
- 需要提前安装一个
loader
vue-loader
和vue-template-compiler
。
npm install vue-loader vue-template-compiler --save-dev
- 在
webpack.config.js
中配置vue-loader
。
{
test: /\.vue$/,
use: {
loader: 'vue-loader'
}
}
- 创建一个
Vue component
:
<template>
<!-- 模板相关 -->
<div>
<h2>使用 JS 方式分离 template </h2>
<h2>{{message}}</h2>
<button @click="btnClick">点我啊</button>
</div>
</template>
<script>
export default {
name: "app"
/* js相关 */,
data() {
return {
message: 'Hello webpack! i`m template 666 '
}
},
methods: {
btnClick() {
console.log('你点了我');
}
}
};
</script>
<style scoped>
/* 样式相关 */
h2 {
color: #00f !important;
}
</style>
- 注意
vue-loader
的版本问题。改为13.0.0
。
3.13 认识 plugin
为我们的代码添加一个版权声明插件 BannerPlugin
- 在
webpack.config.js
中配置。
打包html的plugin html-webpack-plugin
- 安装
html-webpack-plugin
插件:
npm install html-webpack-plugin --save-dev
- 配置插件并指定打包模板:
- 可能会出现版本问题 :在
package.json
中修改版本信息
"html-webpack-plugin": "^3.0.0"
npm install
压缩JS代码 UglifyjsWebpackPlugin
-
UglifyjsWebpackPlugin
, 丑化代码,压缩代码插件。
- 安装插件,为开发时插件。
npm install uglifyjs-webpack-plugin@1.1.1 --save-dev
- 在
plugins
中配置插件 :
3.14 使用webpack搭建本地服务器
- 安装
webpack-dev-server
:
npm install --save-dev webpack-dev-server@2.9.3
- 配置
devServer
:
- 启动有两种方式: 第一种使用在
package.json
中的命令的方式,第二种方式使用node_modules
中的命令的方式。
// 第一种方式 由于在 package.json中配置了script命令所以可以直接使用如下命令启动
npm run dev
3.15 webpack 中配置文件的抽离
创建一个
build
文件夹,在里面创建base.config.js
和prod.config.js
、dev.config.js
。将公共的配置都抽取到
base.config.js
中。将开发环境的配置配置到dev.config.js
中 ,将生产环境的配置配置到peod.config.js
中。
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'bundle.js',
/* 有关url的都会加上dist路径 */
// publicPath: 'dist/'
},
module: {
rules: [
{
test: /\.css$/,
/* css-loader 只负责将css文件进行加载 */
/* style-loader 负责将样式添加到DOM中 */
/* loader 从右向左读 */
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "less-loader" // compiles Less to CSS
}]
},
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
/* 当图片小于指定的尺寸的时候回将其转换为base64位的图片 */
limit: 10240,
name: 'img/[name].[hash:8].[ext]'
}
}
]
},
{
test: /\.js$/,
/* exclude 是排除的意思 */
/* include 是包含的意思 */
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['es2015']
}
}
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
plugins: [
new webpack.BannerPlugin('最终版权归我所有!'),
new HtmlWebpackPlugin({
template: 'index.html'
})
]
};
- 安装合并插件,将他们分别进行合并。
npm install webpack-merge@3.0.0 --save-dev
- 将
base.config.js
与prod.config.js
合并:
const UglifyJsWebpackPlugin = require('uglifyjs-webpack-plugin');
const webpackMerge = require('webpack-merge');
const baseConfig = require('./base.config')
/* 合并 基本的配置 + 生产时依赖 */
module.exports = webpackMerge(baseConfig, {
plugins: [
new UglifyJsWebpackPlugin()
]
});
- 将
base.config.js
与dev.config.js
合并:
const webpackMerge = require('webpack-merge');
const baseConfig = require('./base.config')
module.exports = webpackMerge(baseConfig, {
devServer: {
contentBase: './dist',
/* 是否实时进行监听 */
inline: true
}
})
合并完成删除
webpack.config.js
,此时再使用npm run dev
或者npm run build
就会出现找不到命令的问题。解决合并文件只有删除
webpack.config.js
再使用npm run dev
或者npm run build
脚本命令出现的问题。修改package.json
。