包含了Render函数使用,自带插件使用方法,组件及webpack相关内容
安装
使用yarn$ yarn global add vue-cli
$ vue init webpack vuepro
底下一堆测试的选NO
进目录yarn
安装依赖文件yarn run dev
即可运行
Render函数
前言
Virtual Dom
Object –> render虚拟节点 –> createEle(h)基于虚拟节点创建Dom节点 –> diff状态更新后对比,生成补丁 –> patch遍历补丁,更新Dom节点
创建VNode
1 | var VNode = { |
解决场景
代码冗长,大部分代码相同,外城必须包含无用的<div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14<template>
<div>
<h1 v-if="level === 1">
<a :href="#" + title>
<slot></slot>
</a>
</h1>
<h2 v-if="level === 2">
<a :href="#" + title>
<slot></slot>
</a>
</h2>
</div>
</template>
食用
1 | render: function (createElement) { |
约束
所有的组件树中,如果 VNode 是组件,或含有组件的 slot,那 VNode 必须唯⼀。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var Child = {
render: function(creatElement){
return creatElement('p', 'text)
}
}
Vue.component('ele',{render: function (creatElement){
var ChilNode = creatElement(Child)
rentrn creatElement('div',[ChilNode,ChilNode])
//不允许,因为子组件重复了
//或者
return creatElement('div',
[this.$slots.default,this.$slots.default])
//子组件包含组件且重复
//<ele><div><Child></></></>
//对应HTML,Child为component新创建的子组件,不是上面那个
}})
解决办法
1
2
3
4
5
6//第一个,重复5次,每次返回创建节点
return createElement('div',
Arrat.apply(null,{length:5}).map(function () {
return createElement(Child)
})
//第二个,slot克隆
使用JS
Render中没有指令(v-bind),使用JS原生实现。e
v-for
1 | render: function (h) { |
v-if
1 | render: function (h) { |
v-model
1 | render: function (h) { |
slot默认内容,$slots.default
等于undefined
说明⽗组件中没有定义slot
1
2
3if (this.$slots.default === undefined) {
return createElement('div', '没有slot时显示的文本')
}
函数化组件
Vue.js 提供了⼀个 functional 的布尔值选项,设置为 true 可以使组件⽆状态和⽆实例,也就是没有data 和 this 上下⽂。这样⽤ render 函数返回虚拟节点可以更容易渲染,因为函数化组件只是⼀个函数,渲染开销要⼩很多。
使用函数化组件时(export defaule),Render 函数提供了第二个参数 context 来提供临时上下⽂。组件需要的 data、props、slots、children、parent 都是通过这个上下⽂来传递,⽐如 this.level 要改写为context.props.level,this.$slots.default 改写为context.children。
组件
注册
全局
1 | Vue.component: ('my-component', { |
局部
1 | var Child = { |
其他
当tbody
等标签使用的话使用is="my-compontent"
达到效果
模板内使用来双向绑定数据,
data(){return{message:'1'}}
事件监听&数据传递
自定义事件
.native 修饰符,表示监听的是一个原生事件,监听的是该组件的根元素。<my-component v-on:click.native="handleClick"></my-component>
$event获取事件值
父组件
1
2
3
4
5
6
7
8
9// 参数为 传递字符串 事件的值
<div @creat="cpuData('cpu', $event)"></div>
// attr == 'cpu' $event = 事件对象(包含数据)
methods: cpuData(attr, value) {
return this.$store.commit('Shop', {
attr,
value
})
},
子向父
1 | <my @creat="父组件定义的事件"></my> |
父向子
1 | <my-component :init-count="count"></my-component> |
子组件使用props
获取父组件传递的数据,由data
定义this.count
传递给template
,修改时在methods
中。由标签的:init-count="count"
接受父组件数据。
在 JS 中对象和数组是引用类型,指向同一个内存空间,所以 props 是对象和数组时,在子组件内改变是会影响父组件的。
v-model
接受value属性,在有新value时触发input事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<my v-model="value"></my>
//value为实例(父组件)value
//父向子传递
<button @click="hand"></button>
//父组件
hand () {修改数据}
data {value: 1}
//子组件
props必须是value名数据
this.$emit('input', this.changeValue)传出数据
//监听value值,双向绑定数据
watch: {
value(val){ this.changeValue = val}
}
非父子通信
BUS总线&父链子链方法
使用一个空Vue实例当中间件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21const bus = new Vue();
Vue.component('com', {
template: '<button @click="hand">传递事件</button>',
methods: {
hand () {
bus.$emit('message', '来自组件com的内容')
}
}
})
new Vue({
data: {
count : 1
},
mounted () {
bus.$on('message', (msg) => {
this.message = msg;
})
}
})
父链子链(不常用,应急方法)
父组件内
拿到子组件,多个为数组
this.$children
使用方法
this.$children.hand ()
访问数据
this.$children.msg
多个com区分,ref属性
<com ref="a"></com>
this.$refs.a
Vuex
数据验证
1 | props: { |
slot内容分发
创建&获取
1 | <app> |
递归组件
1 | <app :count="1"></app> |
内联模板
子组件上加上inline-template
属性,模板由父组件定义。
导致作用域改变,message由子组件定义
1
2
3
4
5
6
7
8
9
10 <app inline-template>
<div>{{ message }}</div>
</app>
Vue.component('app', {
template: '<div>\
<slot>1</slot>\
</div>',
data() {return{message:'你好'}}
})
效果显示你好
异步组件
1 | Vue.component('app',(resolve, reject) => { |
$nextTick
v-if显示的div在未显示时拿不到内容
可以提前渲染
1
2
3
4
5new Vue({
el: '#app'
created () {
this.$nextTick(() => {})
}})
x-template
在未使用webpack等工具构建时,写template中写标签很麻烦
1
2
3
4
5
6
7
8<my-component></my-component>
//在body中直接写入
<script type="text/x-template" id="my-component">
<div></div>
</script>
Vue.component('my-component',{template: '#myComponent'})
手动挂载实例
1 | const myCom = Vue.extend({ |
插件篇
全局注册组件
1 | MyPlugin.install = function (Vue, options) { |
vue-router
官方文档
以下为安装vue时附带安装vue-router的配置文件,在src/router
里。已做修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31import Vue from 'vue'
import VRouter from 'vue-router'
Vue.use(VRouter)
// 多路由,异步加载
const Routers = [
{
path: '/index',
component: (resolve) => require(['../components/Hello'], resolve)
},
{
path: '/hello',
component: (resolve) => require(['../components/Hello1'], resolve)
},
// 通配符,设置未指定页面的指向页面
{
path: '*',
redirect: '/index'
},
{
path: '/hello/:id',
component: (resolve) => require(['../components/user.vue'], resolve)
},
]
// 开启history模式
export default new VRouter({
mode:'history',
routes: Routers
})
user.vue文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28// 获取到id
<div>
{{ $route.params.id}}
</div>
// 页面跳转
// 动态跳转 :to=变量
// 换标签 tag="li"
// 不保存历史记录 replace
// 点击后切换class active-class="calssname"
<router-link to="/hello">跳转到Hello</router-link>
//第二种方法,用js
<button @click="hand">跳转到user </button>
<script>
// 获取到id
this.$route.params.id
methods: {
hand () {
this.$router.push('/user/123')
this.$router.replace('/user/123') // 不写入记录
this.$router.go(-1) // 前进后退
}
}
</script>
改变tittle,切换页面后滚动条位置重置,检测是否登录,
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 点击切换时触发
// to 即将要进入的路由对象
// from 要离开的路由对象
// next 主动调用,进入下一个生命周期
// next(false) 取消导航
// 改变tittle
router.beforeEach((to, from, next) => {
window.document.title = to.meta.title; // 获取路由中的meta信息
next();
//检测是否登录,未登录跳转到登录页面
if (window.localStorage.getItem('token')) {
next()
} else {
next('/login')
}
})
// 切换之后触发
// 滚动条重置
router.afterEach( (to, from, next) => {
window.scrollTo(0, 0);
})
router配置
1
2
3
4
5
6
7{
path: '/index',
meta: {
title: '首页'
}
component: (resolve) => require(['../components/Hello'], resolve)
}
Vue特殊方法
阻止事件冒泡
@click
改成`@click.stop`
阻止与默认行为
将@contextmenu
改成`@contextmenu.prevent`
Vuex
安装
yarn add vuex
,新建 store
目录,index.js中写入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 所有应用级别的状态都放在这里
const state = {
// 计数器的初始状态为 0
count: 0
}
// 类似于组件中的 computed,可以对 state 里面的数据
// 做处理,然后通过 $store.getters.func 的方式获取
const getters = {
}
// state 里面的状态不能直接改变,想要改变就需要在这里
// 定义函数,函数的第一个参数是 state,通过这个参数改
// 变 state 里面的数据
// 在组件中通过 $store.commit() 触发
// 类似于methods
const mutations = {
}
// 和 mutations 类似,但 actions 触发的是 mutations
// mutations 只能进行同步操作,在 actions 中可以定义
// 异步操作
const actions = {
}
export default new Vuex.Store({
state,
getters,
mutations,
actions
})
然后main.js中写入
1
2
3
4
5import store from './store'
//添加
new Vue({
store,
})
操作方式
state & mutations
1 | // div中 |
约定 state 只能读取不能修改,实现 count++ 所以在mutations内添加
1
increment: state => state.count++,// 可以接受多个参数({a},{b})
home.vue中
1
<button type="button" @click="$store.commit('increment')">+</button>
或者定义方法
1
2
3
4
5
6
7<button type="button" @click="hand">+</button>
methods: {
hand () {
return this.$store.commit('increment')
}
}
getters
getters
相当于 vuex 的 computed,可以返回对 state 中数据处理后的结果,定义一个函数返回当前的 count 是基数还是偶数
1
2// 接收 state 作为参数
evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
home.vue中调用
1
2
3
4Count为 {{ $store.state.count }} count is {{ $store.getters.evenOrOdd }}
// script
this.$store.getters.evenOrOdd
actions
异步操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27const actions = {
//多值传入
asyncMore: ({ commit }, { amount }) => {
setTimeout(() => {
commit('incrByAmount', { amount })
}, 500)
},
//单值传入
asyncOne: ({ commit }) => {
setTimeout(() => {
commit('increment')
}, 500)
},
//Promise方法
asyncP(context) {
return new Promise(resolve => {
setTimeout(() => {
context.commit('increment')
resolve()
}, 500)
})
},
//不用Promise
asyncP(context, callback) {
setTimeout(() => {callvack()}, 500)
},
}
home中写入
1
2
3
4
5
6
7
8
9
10
11
12<button type="button" @click="asyncMore">异步多值</button>
<button type="button" @click="$store.dispatch('asyncOne')">异步</button>
<button type="button" @click="asyncP">异步(Promise)</button>
asyncMore () {
return this.$store.dispatch('asyncMore', { amount: 10 })
},
asyncP () {
return this.$store.dispatch('asyncP').then(() => {
console.log(this.$store.dispatch('asyncP'))
})
}
modules
配置多了后不够优雅,使用 modules 将 store 分发,每个 modules 有自己的配置
1
2
3
4
5const moduleA = {state:{}}// store的配置
const store = new Vuex.Store({
modules: {a: moduleA}
})
使用
1
this.$store.state.a.xxx
moduleA获取的数据是自身的 state
moduleA 访问 store内的数据,avtions 和 getters可用
1
2
3
4const moduleA = {getters: {
// 自身的state/自身其他的getters/根节点State
a (state, getters, rootState){}
}}
WebPack
基础
- 安装
yarn webpack --save
- 添加到:devDependencies
cnpm install <package_name> --save
cnpm i -D <package_name>
- 添加到:dependencies
cnpm install <package_name> --save-dev
配置
webpack.config.js
出入口文件
1 | //webpack.config.js |
导入模块
如果入口导入一个.css文件,就通过style-loader
和css-loader
转换再打包,
use中从后往前编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.css$/,
use: ExtPlugin.extract({
use: 'css-loader',
fallback: 'style-loader'
})
},
{ //解析vue中的css
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
css: ExtPlugin.extract({
use: 'css-loader',
fallback: 'vue-style-loader'
})
}
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/ //忽略无需编译的内容
},
{
// loader: 'babel-loader?limit=1024'
//如果文件小于1kb则转为base64加载
}
]
},
plugins: [ //插件
new ExtPlugin('main.css') //导出后的css文件名
]
.babelrc
js的配置文件
1
2
3
4
5{
"presets": ["es2015"], //使用es2015
"plugins": ["transform-runtime"],
"comments": false //
}
package.json
包管理
- devDependencies 是你开发时候用的库, 比如测试库,测试服务器之类的,在真实生产环境是不需要的。
- dependencies 是你生产环境需要的依赖库。
- 如果你使用了一些构建工具,比如webpack之类的,打包的时候,是不会把dev库打进去的。
局域网访问监听服务
"scripts" : {"dev": "webpack-dev-server --host 本机IP --port 8888" --open --config webpack.config.js }
main.js
实时修改document.getElementById('app').innerHTML = 'hello'
页面会被修改为hello,覆盖原页面的字符
index.html
使用sass
- 安装对应的模块
cnpm install node-sass --save-dev
node-sasscnpm install sass-loader --save-dev
转换为css插件 可选npm install --save-dev extract-text-webpack-plugin
让webpack可以输出css格式的文件 打开
webpack.base.config.js
加上1
2
3
4{
test: /\.scss$/,
loaders: ["style", "css", "sass"]
},用scss的地方写
1
2
3<style lang="scss" scoped="" type="text/css">
@import '../../static/test.css';
</style>
使用stylus
1 | "stylus-loader": "^2.5.0", |
不多说什么,依赖两个,只用嵌套层的话比sass那个诡异下载好得多
json-server
执行cnpm install json-server --save
打开dev-server,新增jsonServer
1
2
3
4
5
6
7
8
9
10const jsonServer = require('json-server')
const apiserver = jsonServer.create()
const apirouter = jsonServer.router('db.json')
const middlewares = jsonServer.defaults()
apiserver.use(middlewares)
apiserver.use('/api', apirouter) //第一个参数改变内置路由
apiserver.listen(3000, () => {
console.log('JSON Server is running')
}) //3000为端口号设置数据文件
根目录创建db.json
格式如下1
2
3
4
5
6
7
8
9{
"getboardList": [
{
"title": "开放产品",
"description": "开放产品是一款开放产品",
"id": "car",
"toKey": "analysis",
"saleout": false
},设置代理 打开
config/index.js
1
2
3proxyTable: {
'/api': 'http://localhost:3000/'
},
之后可以开始访问/api
来访问jsonServer
一些思路清奇的用法
vuex导入数据,可以直接使用
1 | import { mapGetters } from 'vuex' |
神一样的解构赋值
1 | let { |
直接将某些插件挂载到prototype上使用
1 | Vue.prototype.$error = function(msg) { |