Git Product home page Git Product logo

vue-tutorial's Introduction

vue-tutorial's People

Contributors

wscats avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vue-tutorial's Issues

vuex

Vuex

Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

其实最简单理解为,在我们写Vue组件中,一个页面多个组件之间想要通信数据,那你可以使用Vuex

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
  • Vuex状态管理 === 管理组件数据流动 === 全局数据管理
  • Vue的全局数据池,在这里它存放着大量的复用或者公有的数据,然后可以分发给组件
  • Vue双向数据绑定的MV框架,数据驱动(区别节点驱动),模块化和组件化,所以管理各组件和模块之间数据的流向至关重要
  • Vuex是一个前端非持久化的数据库中心,Vuex其实是Vue的重要选配,一般小型不怎么用,大型项目运用比较多,所以页面刷新,Vuex数据池会重置

路由-》管理的是组件流动

Vuex-》管理的是数据流动

没有Vuex之前,组件数据来源

  • ajax请求后端
  • 组件自身定义默认数据
  • 继承其他组件的数据
  • (从vuex拿)

安装

安装必须的依赖

yarn add vuex --save
npm install vuex --save

使用

在一个模块化的打包系统中,您必须显式地通过Vue.use()来安装 Vuex:

import Vue from 'vue'//var Vue = require('vue')
import Vuex from 'vuex'//var Vuex = require('vuex')
Vue.use(Vuex)

五大概念

  • State 池 (数据源)
  • Getter 查 (获取数据源的数据)
  • Mutation 改 (真正修改的动作)
  • Action 触发修改行为(多配合异步使用)
  • Module 可以拥有多个数据源(数据池)

导出store

每一个 Vuex 应用的核心就是 store(仓库)。store 基本上就是一个容器,它包含着你的应用中大部分的状态(state)。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  2. 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交(commit) mutations。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
// 实例化Vuex,创建一个仓库
const store = new Vuex.Store({
    // 状态
    // 该库存数据的地方
    state: {
        // 状态值
        count: 0,
        author: 'lemon'
    },
    // 修改数据的方法
    mutations: {
        // 修改数据的第一个方法
        increment(state) {
            state.count++
        }
    }
})
// 暴露store仓库到`main.js`的根容器里面
export default store

挂载仓库

// 引入仓库
import store from './configs/store.js'
// Root容器
let vm = new Vue({
  el: "#lemon",
  // 挂载路由
  router,
  // 挂载仓库
  store,
  // 把App组件挂载到#app的这个节点上
  render: h => h('router-view'),
})

获取数据源的数据

第一种方法

beforeCreate() {
    // 来自于vuex仓库
    console.log(this.$store.state.author)
    // 来自于组件自定义
    console.log(this.title)
}

第二种方法

先定义store中的getters

getters: {
    // 获取author的方法
    getAuthor(state) {
        return state.author + ' Yeah'
    },
    getCount(state) {
        return state.count
    }
}

通过属性访问
Getter 会暴露为store.getters对象,你可以以属性的形式访问这些值

computed: {
    // 从仓库里面去取值
    title(){
      return this.$store.getters.getAuthor
    }
}

修改数据的方法

先定义store中的mumat

// 修改数据的方法
mutations: {
    // 修改数据的第一个方法
    editCount(state) {
        state.count++
    },
    // 修改仓库中state中的author
    editAuthor(state, data) {
        state.author = data
    }
}

然后在组件触发commit事件

methods: {
    editAuthor() {
      console.log(this.$store);
      // 触发store里面的mutations,把store里面的author改为laotian
      this.$store.commit('editAuthor','laotian')
    }
}

组件间数据的流动

组件(异步) > dispatch > 触发action > commit > 触发mutations > 修改state的数据
组件(同步) > > > > > commit > 触发mutations > 修改state的数据
组件 > getters > 触发getters > 获取state的数据
const store = new Vuex.Store({
	state: {
		getSearchName: 'hello vuex, I am wscats',
	},
	//接受组件commit传过来的数据并保存到state中,this.$store.commit('changeSearchName', this.searchName);
	mutations: {
		changeSearchName: function(state, a) {
			state.searchName = a;
		},
	},
	//可以从组件调用此方法获取值,一般配合计算属性动态获取值
	//(1)return this.$store.state.searchName
	//(2)return this.$store.getters.getSearchName
	getters: {
		getSearchName: function(state) {
			return state.searchName;
		}
	}
})

视图中获取getSearchName值然后触发**search()**函数,看下面的this.$store.commit('changeSearchName', this.getSearchName);函数

<input type="search" @keyup="search()" v-model="getSearchName" placeholder="搜索" />

通过在根实例中注册store选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store访问到

const app = new Vue({
	store
}).$mount('#app')

往state设置值

直接设置state的值

this.$store.state.title = “wscats”

直接触发mutations

通过commit方法提交直接触发mutations

methods: {
	search() {
		this.$store.commit('changeSearchName', this.getSearchName);
	}
},

触发actions

定义action,再用commit触发mutations,建议如果有异步的操作的话,可以配合action触发数据变动,但是如果是同步的话,可以在组件直接commit触发mutations

actions: {
	setChange(context, data) {
		context.commit('setCount', data)
		context.commit('settitle', data)
	},
	setNews(context, data) {
		context.commit('setNews')
	}
}

然后在组件中用dispatch触发action的改变

methods: {
	loadMore() {
		this.$store.dispatch("setNews")
	}
}

从state获取值

直接获取state的值

不推荐这种做法

computed: {
	searchName() {
		return this.$store.state.searchName;
                //或者return this.$store.state.searchName
	}
},

通过getters方法获取

先在store中定义getters

getters: {
	getTitle(state) {
		//处理数据
		return state.title + "ed"
	}
}

然后通过getters方法配合computed计算属性动态获取

computed: {
	title() {
		//直接获取状态
		//this.$store.state.title
                //通过getters获取状态
		return this.$store.getters.getTitle
	}
}

mapState

我们可以使用mapState来获取vuex里面的state的值,这样比getters方便很多

import { mapActions, mapState } from "vuex";
computed: {
    // 从仓库里面去取值
    title() {
      return this.$store.getters.getAuthor;
    },
    ...mapState({
      author: state => state.author,
    })
}

参考文档

vue引入swiper

swiper

npm install swiper

image

在项目组件中引入swiper模块

import 'swiper';//注意这里只是引入swiper.js
import '../node_modules/swiper/dist/css/swiper.css'//还要单独引入样式,找到node_modules单独把样式引进来

轮播图组件
而初始化的代码和配置参数放进mounted () {}函数里面即可

<template>
	<div class="swiper-container">
		<div class="swiper-wrapper">
			<div class="swiper-slide"><img src="../public/images/wscats.jpg" /></div>
			<div class="swiper-slide"><img src="../public/images/wscats.jpg" /></div>
			<div class="swiper-slide"><img src="../public/images/wscats.jpg" /></div>
		</div>
		<!-- Add Pagination -->
		<div class="swiper-pagination"></div>
	</div>
</template>
<script>
	import 'swiper';
	import '../node_modules/swiper/dist/css/swiper.css'
	export default {
		data() {
			return {
				
			}
		},
		mounted() {
			var swiper = new Swiper('.swiper-container', {
				pagination: '.swiper-pagination',
				paginationClickable: true
			});
		}
	}
</script>
<style>
	.swiper-container {
		width: 100%;
		height: 100%;
	}
	
	.swiper-slide {
		text-align: center;
		font-size: 18px;
		background: #fff;
		/* Center slide text vertically */
		display: -webkit-box;
		display: -ms-flexbox;
		display: -webkit-flex;
		display: flex;
		-webkit-box-pack: center;
		-ms-flex-pack: center;
		-webkit-justify-content: center;
		justify-content: center;
		-webkit-box-align: center;
		-ms-flex-align: center;
		-webkit-align-items: center;
		align-items: center;
	}
	
	.swiper-slide img {
		width: 100%;
		height: 200px;
	}
</style>

image

vue-awesome-swiper

vue-awesome-swiper
DEMO

vue自定义指令

全局定义

我们可以用Vue.directive来全局定义一个指令,定义完之后我们就可以在对应html结构前缀v-加上directive名字来激活这个指令

  • bind:只调用一次,在指令第一次绑定到元素上时调用。
  • update: 在 bind 之后立即以初始值为参数第一次调用,之后每当绑定值变化时调用,参数为新值与旧值。
  • unbind:只调用一次,在指令从元素上解绑时调用。
Vue.directive('cclick', {
	//bind就是先帮我们绑定,但不去执行这个test()函数,而updata则是绑定完后执行一次,后面每次更新都去执行一次
	bind: function(value) {
		var self = this;
		//指令的名字,不包含前缀 v-cclick:click="test()"中的cclick
		console.log(this.name)
		//指令的表达式,不包括参数和过滤器 v-cclick:click="test()"中的test()
		console.log(this.expression)
		//指令的参数 v-cclick:click="test()"中的click
		console.log(this.arg)
		this.el.addEventListener(this.arg, function(e) {
			self.expression.substring(0, self.expression.length - 2);
			//把test()转化为test,方便我们后面调用
			//console.log(self.expression.substring(0, self.expression.length - 2));
			//如果v-cclick:click="test",注意test没有加括号的时候我们就可以这样写
			//self.vm[self.expression]()
			self.vm[self.expression.substring(0, self.expression.length - 2)]()
		});
	},
})
var demo = new Vue({
	el: '#demo',
	data: {
		name: 'wsscat'
	},
	methods: {
		test: function() {
			console.log("test test")
		}
	},
})

视图如下

<body id="demo">
	<button v-cclick:click="test()">Ok</button>
</body>

注意这里我们该指令对应的标签受demo构造器控制,我们可以用this.expression获取函数的名字,然后用this.vm来拿到构造器里面的数据进而触发我们想要触发的方法

在这里要注意的是如果把逻辑放在了update里面定义的话test()会首先先自执行一次
当然其实我们直接这样写<button v-cclick:click="test">Ok</button>_test_没有了两个大括号,我们获取这个函数名再去执行就更加方便了,我们就可以直接这样来执行函数了self.vm[self.expression]()

局部定义

我们还可以在构造起里面定义指令,这样就不会担心影响到其他构造器的指令了,只要在加上属性directives就可以对它进行定义了

var demo = new Vue({
	el: '#demo',
	data: {
		msg: 'hello!',
		name: 'wsscat'
	},
	directives: {
		wsscat: {
			bind: function() {
				console.log("wsscat")
			},
			update: function(newValue, oldValue) {
				console.log('new:' + newValue);
				console.log('old:' + oldValue);
				this.el.style.color = 'blue';
				if(newValue == 'red') {
					this.el.style.color = 'red';
				} else if(newValue == 'yellow') {
					this.el.style.color = 'yellow';
				} else {
					this.el.style.color = 'green';
				}
			}
		}
	}
})

视图如下

<input v-model="name" />
<p v-wsscat="name">{{name}}</p>

上面的代码我们把逻辑写在update里面,因为我们这里的name值会一直变化,第一次进来先触发一次update里面的逻辑,后面每一次name的变化都会触发update里面的逻辑,我们这里在输入框对应输入red,yellow都会有颜色的变化,所以这里update符合需求

vue 关于museui下拉刷新组件

组件-InfiniteScroll

<template>
	<div class="demo-infinite-container" :style="{height:screenHeight-312+'px'}">
		<mu-list v-color="{screenHeight:screenHeight,set_more:set_more}">
			<template v-for="newst in newsts">
				<mu-list-item :title="newst.title" />
				<mu-divider/>
			</template>
		</mu-list>
		<p @click="get_newst_news()" class="mu-item" v-show="more" style="margin: 0px;">查看更多</p>
		<mu-infinite-scroll :scroller="scroller" :loading="loading" @load="get_newst_news" />
	</div>
</template>

<script>
	export default {
		data() {
				const list = []
				for(let i = 0; i < 10; i++) {
					list.push('item' + (i + 1))
				}
				return {
					color: "red",
					list,
					num: 10,
					loading: false,
					scroller: null,
					newsts: [{
						title: 'aa'
					}, {
						title: 'bb'
					}],
					screenHeight: window.screen.height,
					more: false
				}
			},
			mounted() {
				this.scroller = this.$el;
				this.get_screen_height();
				this.get_newst_news();
				this.$nextTick(() => {
					console.log($('.mu-list').offsetHeight)
				})
			},
			directives: {
				color: {
					update: function(el, binding, v) {
						$(el).addClass('red')
						el.offsetHeight
						setTimeout(function() {
							console.log(el.offsetHeight)
							console.log(binding.value.screenHeight - 312)
							if(el.offsetHeight < binding.value.screenHeight - 312) {
								binding.value.set_more(true)
							} else(
								binding.value.set_more(false)
							)
						}, 0)
						console.log(el.offsetHeight)
						console.log(binding)
						console.log(v)
					}
				}
			},
			methods: {
				get_screen_height() {
					var self = this;
					window.addEventListener("resize", function() {
						console.log(window.screen.height)
						self.screenHeight = window.screen.height;
					})
				},
				set_more(bool) {
					console.log("set_more", bool)
					this.more = bool
				},
				loadMore() {
					this.loading = true
					setTimeout(() => {
						for(let i = this.num; i < this.num + 10; i++) {
							this.list.push('item' + (i + 1))
						}
						this.num += 10
						this.loading = false
					}, 2000)
				},
				get_newst_news() {
					this.loading = true
					var self = this;
					$.ajax({
						type: 'GET',
						url: 'http://localhost:81/vue-news/vue/newsts.php',
						dataType: 'jsonp',
						jsonpCallback: 'GET_NEWST',
						success: function(data) {
							console.log(data)
							self.newsts = self.newsts.concat(data.list)
							self.loading = false
						}
					})
				}
			}
	}
</script>

<style lang="css">
	.demo-infinite-container {
		width: 100%;
		/*height: 300px;*/
		overflow: auto;
		-webkit-overflow-scrolling: touch;
		/*border: 1px solid #d9d9d9;*/
	}
</style>

vue-aplayer

安装

Node

$ yarn add vue-aplayer

或者如果你选择 npm

$ npm i vue-aplayer

CDN

<script src="//cdn.jsdelivr.net/npm/vue-aplayer"></script>
<script>
  Vue.component('aplayer', VueAPlayer)
</script>

运行时要求

使用

<aplayer autoplay
  :music="{
    title: 'secret base~君がくれたもの~',
    artist: 'Silent Siren',
    src: 'https://moeplayer.b0.upaiyun.com/aplayer/secretbase.mp3',
    pic: 'https://moeplayer.b0.upaiyun.com/aplayer/secretbase.jpg'
  }"
/>
// ES6
import Aplayer from 'vue-aplayer'

new Vue({
    components: {
        Aplayer
    }
})
import Vue from 'vue'
import Aplayer from 'vue-aplayer'
let Player = Vue.component("Player",{
    template:`
        <aplayer autoplay
            :music="{
            title: '海阔天空',
            artist: 'Beyond',
            src: 'http://zhangmenshiting.qianqian.com/data2/music/3519cdb70c14a95076e8c006c7226963/599516462/599516462.mp3?xcode=6e6d59d18d378711cc3224dc97e4d7a9',
            pic: 'http://qukufile2.qianqian.com/data2/pic/88582702/88582702.jpg@s_1,w_150,h_150'
            }"
        />
    `,
    components:{
        Aplayer
    }
})
export default Player

Props

名称 类型 默认值 说明
music Object 必需 当前播放的音乐。 具体请看音乐信息
list Array [] 播放列表。如果 list 不是空数组,播放列表就会显示出来,即使 list 中只有一首歌并且它和 music 一样
mini Boolean false 迷你模式
float Boolean false 浮动模式。你可以在页面上随意拖放你的播放器
showLrc Boolean false 是否显示歌词
mutex Boolean true 是否在该播放器播放时暂停其他播放器
theme String '#41b883' 主题色。如果当前歌曲也设置了 theme 则以歌曲的为准
shuffle Boolean false 随机播放
repeat String 'no-repeat' 轮播模式。值可以是 'repeat-one'(单曲循环)'repeat-all'(列表循环)或者 'no-repeat'(不循环)。为了好记,还可以使用对应的 'music' 'list' 'none'
listMaxHeight String none 播放列表面板最大高度
listFolded Boolean false 默认收起播放列表
narrow DEPRECATED, 请使用 mini
listmaxheight DEPRECATED, 请使用 listMaxHeight
showlrc DEPRECATED, 请使用 showLrc

如果你是用的是 [email protected]+, 你可以在 music shufflerepeat 上使用 .sync 修饰符

将 Audio 属性作为 props

v1.4.0 开始,我们选取了一些 <audio> 属性并将它们运用为 props

名称 类型 默认值 是否可变 说明
autoplay Boolean false false 自动播放。如果多个 mutex 播放器设置了 autoplay,只有第一个会自动播放
controls Boolean false true 显示原生 audio 元素(在播放器面板和播放列表面板之间)
muted Boolean false true 静音
preload String none true The way to load music, can be 'none' 'metadata' or 'auto'
volume Number 0.8 true 播放音量

mutedvolume 也可以使用 .sync 修饰符,你可以利用这一点做一些自定义的控制

音乐信息

music props 包含了当前播放歌曲的如下信息。

属性 默认值 说明
src 必需 音频文件的 URL
title 'Untitled' 歌曲名称
artist 'Unknown' 演唱者
pic none 封面图片 URL
lrc none LRC 歌词或者歌词文件的 URL
theme none 歌曲的主题色,会覆盖播放器的主题色
url DEPRECATED, 请使用 src
author DEPRECATED, 请使用 artist

事件

v1.4.0 开始, Vue-APlayer 会抛出它内部的 <audio> 元素上触发的所有媒体事件. 你可以查阅 MDN 上的这张完整列表.

v1.4.0 以前, 我们有一些自定义事件,如 play pause canplay playing ended error, 它们现在已全部被废弃。

进阶使用

自适应主题色

v1.3.0 开始, 如果你将一首歌的 theme 值设为 'pic', Vue-APlayer 会从它的封面图片中提取颜色作为主题色.
你也可以直接将 Vue-APlayer 的 theme prop 设为 'pic', 这样所有的歌曲都会使用自适应主题色

你只需将 color-thief 库加入页面中.

注意 color-thief 无法正常使用 npm 安装

<!-- 或者你选择的其他 CDN -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src/color-thief.js"></script>

支持 HLS

v1.3.0 开始, Vue-APlayer 可选支持 *.m3u8 音频. 你需要做的就是在项目中安装 hls.js 包即可.

disableVersionBadge

Vue-APlayer 默认会在控制台打印出当前的版本标识,如果你想要禁用它,可以将 disableVersionBadge 设为 true

import VueAPlayer from 'vue-aplayer'
VueAPlayer.disableVersionBadge = true

Slots

  • slot="display"

这个 slot 代表播放器主体上显示的内容, 默认是滚动歌词.

这个 slot 中的组件会接收两个 props: currentMusicplayStat.

效果

2019-01-16 1 12 31

参考文档

vue-aplayer中文文档

组件

全局注册组件

Vue中的组件可以扩展 HTML 元素,封装可重用的代码,是Vue重要的一部分

//定义组件
var wsscat = Vue.extend({
	template: "<div>I am wsscat</div>"
})
//注册组件
Vue.component('wsscat', wsscat)
var demo = new Vue({
	el: '#demo',
	data: {
		name: 'wsscat',
	}
})

这里注意的是extend和component方法要放在new Vue()之前,不然会报错

局部注册组件

当我们全局注册组件的时候,该组件在任何地方使用,我们也可以局部注册组件,就是在父组件定义即extend时候跟着注册子组件,那么该子组件就只能在父组件中使用

<body id="demo">
	{{name}}
	<wsscat>
		<!--写在里面会被替换掉-->
		<!--<wsscat-child></wsscat-child>
            <wsscat-Child-Second></wsscat-Child-Second>-->
	</wsscat>
</body>
<script>
	var wsscatChild = Vue.extend({
		template: "<p>I'm child</p>"
	})
	var wsscatChildSecond = Vue.extend({
		template: "<span>I'm child2</span>"
	})
	//定义组件
	var wsscat = Vue.extend({
		template: "<div>I'm wsscat<wsscat-child></wsscat-child><wsscat-Child-Second></wsscat-Child-Second></div>",
		replace: true,
		components: {
			//也可以这样写wsscatChild
			'wsscatChild': wsscatChild,
			'wsscatChildSecond': wsscatChildSecond
		}
	})
	//注册组件
	Vue.component('wsscat', wsscat)
	var demo = new Vue({
		el: '#demo',
		data: {
			name: 'wsscat',
		}
	})
</script>

注意下面两种写法等价,当键值对都是同一个的时候就可以这样写

components: {
	wsscatChild: 'wsscatChild'
}
components: {
	wsscatChild
}

局部注册中定义组件并使用
下面我们就把wsscatChildThird放在wsscat这个组件定义的时候定义并注册使用

var wsscatChild = Vue.extend({
	template: "<p>I'm child</p>"
})
var wsscatChildSecond = Vue.extend({
	template: "<p>I'm child2</p>"
})
//定义组件
var wsscat = Vue.extend({
	template: "<div>I'm wsscat<wsscat-child></wsscat-child><wsscat-Child-Second></wsscat-Child-Second><wsscat-child-third></wsscat-child-third></div>",
	replace: true,
	components: {
		//也可以这样写wsscatChild
		'wsscatChild': wsscatChild,
		'wsscatChildSecond': wsscatChildSecond,
		'wsscatChildThird': {
			template: "<p>I'm child3</p>"
		}
	}
})

编写模版

我们可以用script标签设置type="text/template",并且给一个id名

<script type="text/template" id="tpl">
    <div>Hello {{name}}</div>
</script>

或者我们还可以用template标签定义这个模版

<template id="tpl">
    <div>Hello {{name}}</div>
</template>

我们就可以在template属性绑定id名,就可以读取上面的模版

var wsscat = Vue.extend({
        template: '#tpl',
        data: function() {
            return {
                name: 'Wsscat'
            }
        }
})

组件中传递数据

在组件中我们可以在定义的时候用data属性绑定数据到模版上
注意这里我们使用函数返回一个对象把数据定义出来的

var wsscatChildSecond = Vue.extend({
	template: "<p>I'm child2, I like {{skill}}</p>",
	data: function() {
		return {
			skill: "javascript"
		}
	}
})

组件接收组件外的数据

我们可以用props获取组件所在id="demo"这个作用域下定义的name数值,由于组件wsscat中的name和id=demo的name是互不影响的,所以我们可以用这样的方法把name传进去给wsscat这个组件

var wsscat = Vue.extend({
            template: '#tpl',
            props:['msg'],
            data: function() {
                return {
                    name: 'Wsscat'
                }
            }
})
// 注册
Vue.component('wsscat', wsscat)
        new Vue({
            el: '#demo',
            data: {
                name: 'wsscats'
            }
})

视图,记得msg这个属性前面要加上个冒号,其实就是等同于v-bind:msg缩写为:msg

<div id="demo">
        <wsscat :msg="name"></wsscat>
</div>
<script type="text/template" id="tpl">
        <div>Hello {{name}}</div>
        <div>{{msg}}</div>
</script>

父组件向子组件传递数据

我们只需要在子组件中加一个props属性,props属性接受一个数组,接受子组件标签上的属性,因为子组件上的属性父组件是可以控制的,我们就可以在子组件中获取到父组件的数据
例如下面<wsscat-child :test='sweet'></wsscat-child>
:test相当于v-bind:test加了冒号父组件的数据就会与子组件的数据实现双向数据绑定,当然父组件能控制子组件的值,但是子组件却不能影响父组件的值

var wsscat = Vue.extend({
    template: "<input v-model='sweet' /><div>I'm wsscat<wsscat-child :test='sweet'></wsscat-child><wsscat-Child-Second></wsscat-Child-Second><wsscat-child-third></wsscat-child-third></div>",
    data:function(){
        return {
            sweet:"你好",
        }
    },
    replace:true,
    components: {
        //也可以这样写wsscatChild
        'wsscatChild': wsscatChild,
        'wsscatChildSecond': wsscatChildSecond,
        'wsscatChildThird': {
        	template:"<p>I'm child3</p>"
        }
    }
})
  • v-bind:test``:test默认单项绑定,父能影响子,子不能影响父
  • v-bind:test.sync``:test.sync父子互相能影响
  • v-bind:test.once``:test.once除了第一次赋值,父子互不影响

子组件可以用 this.$parent 访问它的父组件。根实例的后代可以用 this.$root 访问它。
参考文档
例如我们在第一个子组件wsscatChild的ready属性中添加函数,输出this.$parent,就可以在this.$parent.$data中看到父组件wsscat的name值

var wsscatChild = Vue.extend({
	props: ['test'],
	ready: function() {
		console.log(this.$parent);
	},
	template: "<p>I'm child, {{test}}</p><input v-model='test' />"
})

尽管可以访问父链上任意的实例,不过子组件应当避免直接依赖父组件的数据,尽量显式地使用 props 传递数据。另外,在子组件中修改父组件的状态是非常糟糕的做法

vue实现类似angular服务的方法

var kinds = [{
            "id": "1",
        }, {
            "id": "2",
        }, {
            "id": "3",
        }, {
            "id": "4",
        }]
var Click = function() {
            console.log("hello wsscat")
        }
var demo = new Vue({
            el: '#demo',
            data: {
                kinds,
            },
            methods: {
                Click,
            }
        })

上面我们把需要复用的方法和数据在Vue构造器外部并之前定义,就可以达到在每个Vue()里面复用

<ul>
            <li v-for="kind in kinds">
                {{kind.id}}
            </li>
        </ul>
        <button @click="Click()">Wsscat</button>

这种就相当于angular定义的服务可以复用数据和方法

vue render函数

createElement的函数格式,注意只能拥有一个根节点(VNode)

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一个 HTML 标签,组件选项,或一个函数
  // 必须 Return 上述其中一个
  'div',
  // {Object}
  // 一个对应属性的数据对象
  // 您可以在 template 中使用.可选项.
  {
    // (该节点的属性值)
  },
  // {String | Array}
  // 子节点(VNodes). 可选项.
  [
    createElement('h1', 'hello world'),
    createElement(MyComponent, {
      props: {
        someProp: 'foo'
      }
    }),
    'bar'
  ]
)

视图层

<div id="app">
</div>

image

new Vue({
	el: '#app',
	render: function(createElement) {
		let a = 1;
		return createElement('div', {
			style: {
				color: 'red',
				fontSize: '14px'
			}
                        //注意class是關鍵詞
			'class': {
				foo: true,
				bar: false
			}
		}, [
			createElement('h' + a, 'render函數'),
			createElement('p', {
				style: {
					color: 'blue',
					fontSize: '12px'
				}
			}, [
				createElement('span', 'Hello Wscats'), '!'
			]),
		])
	}
})

20 个重复的段落
image

new Vue({
	el: '#app',
	render: function(createElement) {
		return createElement('div',
			Array.apply(null, {
				length: 20
			}).map(function() {
				return createElement('p', 'hi')
			})
		)
	}
})

image

let abc = Vue.extend({
	template:"<p>wscats</p>"
})
//or// let cba = require('./app/cba.vue')
new Vue({
	el: '#app',
	render: function(createElement) {
		return createElement(abc)
	}
})

image

new Vue({
	el: '#app',
	render: function(createElement) {
		return createElement('h1', 'hello world')
	},
})
new Vue({
	el: '#app',
	data: {
		items:[{
			name:'wscat'
		},{
			name:'autumn'
		},{
			name:'eno'
		}]
	},
	render: function(createElement) {
		if(this.items.length) {
			return createElement('ul', this.items.map(function(item) {
				return createElement('li', item.name)
			}))
		} else {
			return createElement('p', 'No items found.')
		}
	}
})

等同于

<ul v-if="items.length">
  <li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>

更多详情可参考官方文档

vue过渡动画

过渡动画

transition 特性可以与下面资源一起用:

  • v-if
  • v-show
  • v-for (只在插入和删除时触发,使用 vue-animated-list 插件)

DEMO

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <style>
        /* 必需 */
        /*伸展动画*/

        .expand-transition {
            transition: all .3s ease;
            height: 20px;
            padding: 10px;
            background-color: #eee;
            overflow: hidden;
        }
        /* .expand-enter 定义进入的开始状态 */
        /* .expand-leave 定义离开的结束状态 */

        .expand-enter,
        .expand-leave {
            height: 0;
            padding: 0 10px;
            opacity: 0;
        }
        /*旋转动画*/

        .rotate-transition {
            transition: all .3s ease;
            height: 20px;
            padding: 10px;
            background-color: #eee;
            overflow: hidden;
        }
        /* .expand-enter 定义进入的开始状态 */
        /* .expand-leave 定义离开的结束状态 */

        .rotate-enter,
        .rotate-leave {
            height: 0;
            padding: 0 10px;
            opacity: 0;
            transform: rotate(360deg);
        }
        /*动画进行时候插入的类*/

        .In {
            background-color: #0062CC;
        }

        .Out {
            background-color: #2AC845;
        }

        .staggered-transition {
            transition: all .5s ease;
            overflow: hidden;
            margin: 0;
            height: 20px;
        }

        .staggered-enter,
        .staggered-leave {
            opacity: 0;
            height: 0;
        }
    </style>

    <body id="demo">
        <!--为了应用过渡效果,需要在目标元素上使用 transition-->
        <!--transition 特性可以与下面资源一起用:
            v-if
            v-show
            v-for (只在插入和删除时触发,使用 vue-animated-list 插件)
        -->
        <p style="color:red">v-if</p>
        <div v-if="fi" transition="expand">hello wsscat</div>
        <button @click="fi=!fi">toggle</button>

        <p style="color:red">v-show</p>
        <div v-show="show" transition="expand">hello wsscat</div>
        <button @click="show=!show">toggle</button>

        <p style="color:red">v-show 动态绑定</p>
        <select v-model="tran">
            <option value="expand">expand</option>
            <option value="rotate">rotate</option>
        </select>
        <div v-show="show1" :transition="tran">hello wsscat</div>
        <button @click="show1=!show1">toggle</button>

        <p style="color:red">v-for</p>
        <input v-model="query">
        <li v-for="item in list | filterBy query" transition="staggered" stagger="100">
            {{item.msg}}
        </li>
    </body>
    <script src="../vue.js"></script>
    <script>
        //全局定义
        //JavaScript钩子,动画发生时候改变动画所在标签的值,enter和afterEnter最为明显
        Vue.transition('expand', {
            beforeEnter: function(el) {
                el.textContent = 'beforeEnter'
            },
            enter: function(el) {
                el.textContent = 'enter'
            },
            afterEnter: function(el) {
                el.textContent = 'afterEnter'
            },
            enterCancelled: function(el) {
                // handle cancellation
            },
            beforeLeave: function(el) {
                el.textContent = 'beforeLeave'
            },
            leave: function(el) {
                el.textContent = 'leave'
            },
            afterLeave: function(el) {
                el.textContent = 'afterLeave'
            },
            leaveCancelled: function(el) {
                // handle cancellation
            }
        })

        new Vue({
            el: '#demo',
            data: {
                name: 'wsscat',
                fi: true,
                show: true,
                show1: true,
                tran: 'expand',
                query: '',
                //v-for的列表数据
                list: [{
                    msg: 'Bruce Lee'
                }, {
                    msg: 'Jackie Chan'
                }, {
                    msg: 'Chuck Norris'
                }, {
                    msg: 'Jet Li'
                }, {
                    msg: 'Kung Fury'
                }]
            },
            //局部定义
            transitions: {
                rotate: {
                    //定义动画进入前后加载的类名
                    enterClass: 'In',
                    leaveClass: 'Out',
                    enter: function(el) {
                        el.textContent = 'Hello'
                    },
                    afterEnter: function(el) {
                        el.textContent = 'Bye'
                    },
                }
            }
        })
    </script>
</html>

首先在在v-if,v-show或者v-for指令上面写上属性transition,格式为transition="类名"(下面例子均为transition="expand")
注意.expand-transition一定要在写在.expand-enter.expand-leave前面,不然enter和leave动画会失效
进场动画的过渡为.expand-enter=>.expand-transition
离场动画的过渡为.expand-transition=>.expand-enter
一般情况下我们可以保持.expand-enter.expand-enter一样

vue

Vue.js 教程 - 极客学院Wiki
Vue.js首先是个MVVM的框架,跟angular在某些地方很相似,它没有控制器的概念,有的是组建和过滤器来处理数据在M跟V层之间的通信

View
用户看到的实际HTML / DOM
demo.$el // The View

Model
这是一个略微修改的Javascript对象
demo.$data // The Model

vue同组件不同路由切换

当我们使用路由的时候,切换不同路由进去同一个组件的时候,想监听这个路由的改变可以运用以下代码
kapture 2018-11-01 at 12 57 20

export default {
  data() {
//...
watch: {
    $route: function(to, from) {
      this.$route.params;
      console.log("路由变化了");
    }
  }
};

vue异步组件

构建异步组件

先定义一个外部的xheader.js来声明一个组件

Vue.component("xheader", {
    template: `
        <header :style="{border:'1px solid red'}">头部组件</header>
    `
})

构建异步加载方法

这个是异步创建script节点实现异步加载.js文件,从而实现异步加载xheader.js文件

// 实现异步加载js文件
function load(componentName, path) {
    return new Promise(function (resolve, reject) {
        var script = document.createElement('script');
        script.src = path;
        script.async = true;
        script.onload = function () {
            var component = Vue.component(componentName);
            if (component) {
                resolve(component);
            } else {
                reject();
            }
        };
        script.onerror = reject;
        document.body.appendChild(script);
    });
}

注册异步组件

new Vue({
    el: "#demo",
    template: `
        <div>
            <p>{{name}}</p>
            <xheader></xheader>
        </div>
    `,
    data: {
        name: "组件"
    },
    components: {
        xheader: function (resolve, reject) {
            // 这里可以用异步方法实现异步加载组件
            setTimeout(function () {
                load('xheader', 'xheader.js').then(resolve, reject);
            }, 3000)
        }
    }
})

参考文档

vue路由原理

hashchange

现在大部分单页面应用程序的路由切换都是以下这种方式

http://www.xxx.com/#/index

这种 #。后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发 hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听 hashchange 来实现更新页面部分内容的操作

<body>
    <a href="#/index">index</a>
    <a href="#/home">home</a>
    <script>
        function matchAndUpdate() {
            // 匹配 hash 做 dom 更新操作
            console.log("路由切换了");
        }
        window.addEventListener('hashchange', matchAndUpdate)
    </script>
</body>

示例

哈希路由的简单实现

vue-cli脚手架

如果是mac系统安装全局包的时候,在npm前面写上sudo,添加权限

sudo npm install -g vue-cli

初始化第一个项目
qq20160820-0

定位到我们的项目文件夹位置

cd my-first-vue-project

然后安装package.json的依赖包

sudo npm install

安装完成之后我们就可以运行服务器运行我们的第一个项目

npm run dev

qq20160820-1
npm run dev能打开服务器是因为我们在生成package.json中定义了一个scripts
qq20160820-2
所以执行npm run dev相当于执行node build/dev-server.js

并在浏览器打开下面的地址

http://localhost:8080

qq20160820-3

vue使用better-scroll

安装

具体可以看官方文档

npm install better-scroll --save
//接下来就可以在代码中引入了,webpack等构建工具都支持从node_modules里引入代码:
import BScroll from 'better-scroll'

在vue框架中使用的具体介绍,可以参考作者这篇文章当 better-scroll 遇见 Vue

引入

HTML部分,以下是很多Vue项目经典的写法

<template>
    <div id="wrapper" ref="list">
      <div id="scroller">
        <XhomeCard :key="index" v-for="(n,index) in news" :content="n" />
      </div>
    </div>
</template>

JS部分,异步数据的处理注意加载的时机,要在数据完全并且得到并且渲染之后,才能开始挂载逻辑,这里的this.$nextTick是一个异步函数,为了确保DOM已经渲染

import BScroll from "better-scroll";
export default {
    methods: {
        // 加载数据
        loadMore() {
            this.$axios
                .get("/api/container/getIndex", {
                    params: {}
                })
                .then(response => {
                    this.$nextTick(() => {
                        if (!this.scroll) {
                            this.scroll = new BScroll(this.$refs.list, {
                                click: true,
                                scrollY: true,
                                pullUpLoad: {
                                    threshold: -30 // 当上拉距离超过30px时触发 pullingUp 事件
                                }
                            });
                            this.scroll.on("pullingUp", () => {
                                // 上拉刷新
                                // 做些事情
                                this.scroll.finishPullUp(); // 事情做完,需要调用此方法告诉 better-scroll 数据已加载,否则上拉事件只会执行一次
                            });
                            this.scroll.on("scrollEnd", () => {
                                // 滚动到底部
                                if (this.scroll.y <= this.scroll.maxScrollY) {
                                    this.loadMore();
                                }
                            });
                        } else {
                            // 这里触发this.scroll刷新,不然如果异步数据更新的话,this.scroll不刷新会置顶回去
                            this.scroll.refresh();
                        }
                    });
                })
                .catch(error => {
                    console.log(error);
                });
        }
    }
}

css部分其实很关键,缺少了也不可以,当然我貌似发现官方文档里面没有写出来,这部分参考的是iscroll的样式

<style scoped>
* {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

html {
  -ms-touch-action: none;
}

body,
ul,
li {
  padding: 0;
  margin: 0;
  border: 0;
}

body {
  font-size: 12px;
  font-family: ubuntu, helvetica, arial;
  overflow: hidden;
  /* this is important to prevent the whole page to bounce */
}

/* 正常情况只要wrapper和scroller类就可以了 */
#wrapper {
  position: absolute;
  z-index: 1;
  top: 84px;
  bottom: 0;
  left: 0;
  width: 100%;
  background: #ccc;
  overflow: hidden;
}

#scroller {
  position: absolute;
  z-index: 1;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  width: 100%;
  -webkit-transform: translateZ(0);
  -moz-transform: translateZ(0);
  -ms-transform: translateZ(0);
  -o-transform: translateZ(0);
  transform: translateZ(0);
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-text-size-adjust: none;
  -moz-text-size-adjust: none;
  -ms-text-size-adjust: none;
  -o-text-size-adjust: none;
  text-size-adjust: none;
}
</style>

上拉刷新

kapture 2018-11-07 at 9 43 22

利用实例化后的this.scroll可以获取滚动的高度,和列表的高度,然后根据指导的顶部导航栏高度来计算出到底的临界点

this.scroll.on("scrollEnd", () => {
    // 滚动到底部
    if (this.scroll.y <= this.scroll.maxScrollY + 50) {}
});

不计算头部

this.scroll.on("scrollEnd", () => {
    // 滚动到底部
    if (this.scroll.y <= this.scroll.maxScrollY) {
        this.loadMore();
    }
})

或者使用

this.scroll.on("pullingUp", () => {
    console.log("上拉刷新");
    // 做些事情
    this.scroll.finishPullUp(); // 事情做完,需要调用此方法告诉 better-scroll 数据已加载,否则上拉事件只会执行一次
});

还有最好做严谨判断,比如路由切换,组件销毁要判断this.$scroll是否存在,没有则重新实例化

this.$nextTick(() => {
    if (!this.scroll) {
        this.scroll = new BScroll(this.$refs.list, {})
    }else{
        this.scroll.refresh();
    }
})

数据更新,可以触发refresh

destoryed() {
    this.scroll.refresh();
    this.scroll = null;
}

下拉刷新

kapture 2018-11-07 at 9 39 51

判断出现下拉刷新是否超出距离,然后触发更新数据,数据加载(concat)数组头部

this.scroll.on("touchEnd", pos => {
    // 下拉动作
    if (pos.y > 32) {
        this.loadMore("down");
    }
});

vue指令

v-text

v-text类似于表达书{{}}的作用,不过以指令的形式写的时候就会像表达式那样在vue未加载完时候显示{{}}

<span v-text="name"></span>
<span>{{name}}</span>

v-html

输出html结构,要注意的是不要用在用户提交的地方,以防XSS攻击

<div v-html="html"></div>
<div>{{{html}}}</div>

v-if

条件判断渲染,跟v-show的区别在于,v-show是改变display的属性值,而v-if是对DOM结构的增删

<p v-if="bool">wsscat</p>
<p v-show="bool">wsscat</p>

v-show

根据表达式的值的真假切换元素的display CSS属性,注意angular有ng-show和ng-hide,但是vue没有v-hide

<p v-if="bool">wsscat</p>
<p v-show="bool">wsscat</p>

v-if&&v-show

前一兄弟元素必须有v-if或v-show,也就是说包含v-if和v-show的标签要紧靠在一起,不然就会失效

<div v-if="Math.random() > 0.5">
  Sorry
</div>
<div v-else>
  Not sorry
</div>

v-for

类似于angular的ng-repeat,可以接收对象,数组和字符串,$index为遍历的索引值

<div v-for="item in items" id="item-{{$index}}">
  {{ item.text }}
</div>

v-on

v-on都可以缩写@代替,例如@click@keyup

<button v-on:click="do()"></button>
<!-- 缩写 -->
<button @click="do()"></button>

v-bind

v-bind可以缩写为:,动态绑定一个或者多个属性值

<img v-bind:src="imgUrl" />
<!-- 缩写 -->
<img :src="imgUrl" />
<a v-bind:href="url">to wsscat</a>
<!-- 缩写 -->
<a :href="url">to wsscat</a>
<!-- 绑定 class -->
<div :class="{ red: isRed }"></div>
<!-- 绑定 style -->
<div :style="{ fontSize: size + 'px' }"></div>

v-model

从视图的标签中获取数据,并把数据绑定回JS
用在下面这几个地方:

<input v-model='wsscat'>
<select v-model='wsscat'>
<textarea v-model='wsscat'>

vue运用lodash自定义过滤器

安装

由于vue2.x版本删除了filterBy等过滤器,我们可以用lodash来模拟过滤器的方法

安装并引用lodash

var Vue = require("vue");
var _ = require("lodash");

使用

页面接受输入框需要搜索的值,和搜索的列表结果

new Vue({
    el: "#demo",
    template: `
		<div>
			<input v-model="search" />
			<select v-model="key">
				<option value="user">user</option>
				<option value="age">age</option>
			</select>
			<ul>
				<li v-for="arr in arrss">name:{{arr.user}}  age:{{arr.age}}</li>
			</ul>
		</div>
	`,
    data: {
        search: "",
        //默认进行名字筛选
        key: "user",
        arrs: [{
            'user': 'wscats',
            'age': 18,
        }, {
            'user': 'oaoafly',
            'age': 16,
        }, {
            'user': 'yao',
            'age': 14,
        }, {
            'user': 'eno',
            'age': 99,
        }]
    }
})

由于过滤器只能用在表达式{{}}中,v-for不能像以前用自定义过滤器配合管道字符|实现过滤筛选,我们选用计算属性来解决这个问题,在计算属性中我们接受输入框的值,并且配合_.filter方法实现弱搜索

computed: {
    arrss() {
        var output;
        if (this.search) {
            //可以传对象或者根据匿名函数的条件进行筛选结果
            output = _.filter(this.arrs, (item) => {
                //遍历每项字符串
                if (_.isString(item[this.key])) {
                    return item[this.key].indexOf(this.search) != -1;
                } else {
                    //数字强制转换为字符串
                    return (item[this.key] + "").indexOf(this.search) != -1;
                }
            })
        } else {
            output = this.arrs
        }
        return output;
    }
}

Lodash

更多关于Lodash请参考Wscats/node-tutorial#16

vue源码参考文档

剖析Vue原理&实现双向绑定MVVM
Object.keys可以用来遍历数组或者对象的索引值或者属性值

var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

// array like object with random key ordering
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(an_obj)); // console: ['2', '7', '100']

// getFoo is property which isn't enumerable
var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; } } });
my_obj.foo = 1;

console.log(Object.keys(my_obj)); // console: ['foo']

Object.defineProperty
Object.defineProperty接受三个参数,而且都是必填的,

  • 第一个参数:目标对象
  • 第二个参数:需要定义的属性或方法的名字
  • 第三个参数:目标属性所拥有的特性
var obj= {}
    Object.defineProperty(obj,"name",{
      value:'wsscat'
})
console.log(obj.name);//wsscat

我们可以用set和get监听值的读取和修改,进而触发我们需要的操作

var obj = {}
Object.defineProperty(obj, "name", {
    set: function (newValue) {
        console.log('newValue:' + newValue)
    },
    get: function () {
        console.log("getValue:")
        return '你已经成功取值'
    }
})
obj.name = 'wsscat' //log: newValue:wsscat
console.log(obj.name) //log: getValue:你已经成功取值

Observer

对对象的属性进行遍历并监听值的变化

下面代码我们封装一个observe方法,observe方法执行对obj对象的遍历,每个遍历我们对其属性添加一个defineProperty监听,要注意的是如果属性的值也是对象我们要再对子属性这个对象里面的属性值遍历监听,所以我们再observe方法写了一个判断是否为空和为对象的判断条件

var obj = {
    age: 1,
    skill: 'js',
    name: 'autumns',
    obj: {
        num: 1
    }
}

function observe(obj) {
    //判断对象的自属性是否对象,如果时对象继续执行监听
    if (!obj || typeof obj !== 'object') {
        return;
    }
    Object.keys(obj).forEach(function (key) {
        //console.log(key + '=>' + obj[key]);
        //遍历监听
        defineProperty(obj, key, obj[key]);
    })
}

function defineProperty(obj, key, val) {
    observe(val); //监听子属性,如果子属性也是对象,继续监听
    Object.defineProperty(obj, key, {
        set: function (newValue) {
            console.log('newValue:' + newValue);
            //设置新值
            val = newValue;
        },
        get: function () {
            console.log("getValue:" + val);
            //返回val设置对应属性值
            return val;
        }
    })
}
observe(obj); //对对象遍历并监听每个属性值的变化
obj.obj.num = 2; //log: newValue:2 监听子属性
obj.name = 'wsscat' //log: newValue:wsscat
console.log(obj.name) //log: getValue:你已经成功取值

Compile

这部分是解析模版指令,将模版中的变量变成数据,如同我们使用angular和vue中的表达式**{{变量}},我们就是把表达式里面的变量转化为数据
为了不想重复操作DOM,我们把el作用域下的DOM结构转换成文档碎片
fragment**,并进行解析编译操作,解析完成,再将fragment添加回原来的真实dom节点中,下面的node2Fragment就是实现这个保存DOM结构到文档碎片的功能

function node2Fragment(el) {
    var fragment = document.createDocumentFragment(),
        child;
    // 将原生节点拷贝到fragment
    while (child = el.firstChild) {
        //console.log(el.firstChild)
        //console.log(child)
        fragment.appendChild(child);
    }
    return fragment;
}

解析表达式**{{name}}**,这里分三种情况

//compileElement分支
<p>
    <span>{{name}}</span>
</p>
 //compileText分支
{{my}}
//compile分支
<p v-abc="{{name}}" id="cba">1</p>
compileElement: function (el) {
    var childNodes = el.childNodes,
        me = this;
    [].slice.call(childNodes).forEach(function (node) {
        var text = node.textContent;
        var reg = /\{\{(.*)\}\}/;
        //<p>{name}<p>
        if (me.isElementNode(node)) {
            me.compile(node);
        }
        //{name}
        else if (me.isTextNode(node) && reg.test(text)) {
            me.compileText(node, RegExp.$1);
        }
        //<p><span>{{name}}</span></p>
        if (node.childNodes && node.childNodes.length) {
            me.compileElement(node);
        }
    });
}

vue-cli的vue.config.js

配置文件

Vue-CLI3搭建的项目,目录结构相比2.0有很大的改变,之前的buildconfig文件夹没有了,如果要像以前那样配置webpack的参数,只需要在项目的根目录下新建vue.config.js 文件注意是根目录,不是src目录

module.exports = {
    // 基本路径
    baseUrl: '/',
    // 输出文件目录
    outputDir: 'dist',
    // eslint-loader 是否在保存的时候检查
    lintOnSave: true,
    // use the full build with in-browser compiler?
    // https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only
    compiler: false,
    // webpack配置
    // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
    chainWebpack: () => {},
    configureWebpack: () => {},
    // vue-loader 配置项
    // https://vue-loader.vuejs.org/en/options.html
    vueLoader: {},
    // 生产环境是否生成 sourceMap 文件
    productionSourceMap: true,
    // css相关配置
    css: {
        // 是否使用css分离插件 ExtractTextPlugin
        extract: true,
        // 开启 CSS source maps?
        sourceMap: false,
        // css预设器配置项
        loaderOptions: {},
        // 启用 CSS modules for all css / pre-processor files.
        modules: false
    },
    // use thread-loader for babel & TS in production build
    // enabled by default if the machine has more than 1 cores
    parallel: require('os').cpus().length > 1,
    // 是否启用dll
    // See https://github.com/vuejs/vue-cli/blob/dev/docs/cli-service.md#dll-mode
    dll: false,
    // PWA 插件相关配置
    // see https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
    pwa: {},
    // webpack-dev-server 相关配置
    devServer: {
        open: process.platform === 'darwin',
        host: '0.0.0.0',
        port: 8080,
        https: false,
        hotOnly: false,
        // proxy: null, // 设置代理
        // 配置多个代理
        proxy: {
            "/api": {
                target: "<url>",
                ws: true,
                changeOrigin: true
            },
            "/foo": {
                target: "<other_url>"
            },
            before: app => {}
        },
        // 第三方插件配置
        pluginOptions: {
            // ...
        }
    }
}

参考文档

vue-ssr服务端渲染

安装

  • 推荐使用 Node.js 版本 6+。
  • vue-server-renderervue 必须匹配版本。
  • vue-server-renderer 依赖一些 Node.js 原生模块,因此只能在 Node.js 中使用。我们可能会提供一个更简单的构建,可以在将来在其他「JavaScript 运行时(runtime)」运行。
npm install vue vue-server-renderer express --save

渲染实例

新建一份test.js写入以下代码,并运行node test,将会运行出三段结果<div data-server-rendered="true">Hello World</div>,说明已经成功实现服务端渲染。

// 第 1 步:创建一个 Vue 实例
const Vue = require('vue')
const app = new Vue({
    data: {
        title: 'Hello World'
    },
    template: `<div @click='testClick'>{{title}}</div>`,
    methods: {
        testClick() {
            console.log('Hello World')
        }
    }
})

// 第 2 步:创建一个 renderer
const renderer = require('vue-server-renderer').createRenderer()

// 第 3 步:将 Vue 实例渲染为 HTML
renderer.renderToString(app, (err, html) => {
    if (err) throw err
    console.log(html)
    // => <div data-server-rendered="true">Hello World</div>
})

// 在 2.5.0+,如果没有传入回调函数,则会返回 Promise:
renderer.renderToString(app).then(html => {
    console.log(html)
    // => <div data-server-rendered="true">Hello World</div>
}).catch(err => {
    console.error(err)
})

// await和async
;(async (renderer) => {
    let html = await renderer.renderToString(app)
    console.log(html)
})(renderer)

与服务器集成

在 Node.js 服务器中使用时相当简单直接,例如 Express:

const Vue = require('vue')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer()

server.get('*', (req, res) => {
    const app = new Vue({
        data: {
            url: req.url
        },
        template: `<div>访问的 URL 是: {{ url }}</div>`
    })

    renderer.renderToString(app, (err, html) => {
        if (err) {
            res.status(500).end('Internal Server Error')
            return
        }
        res.end(`
            <!DOCTYPE html>
            <html lang="en">
                <head><title>Hello</title></head>
                <body>${html}</body>
            </html>
        `)
    })
})

server.listen(8080)

使用一个页面模板

当你在渲染 Vue 应用程序时,renderer 只从应用程序生成 HTML 标记 (markup)。在这个示例中,我们必须用一个额外的 HTML 页面包裹容器,来包裹生成的 HTML 标记。

const Vue = require('vue')
const fs = require('fs')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer({
    template: fs.readFileSync('./index.template.html', 'utf-8')
})
server.get('*', (req, res) => {
    const app = new Vue({
        data: {
            url: req.url
        },
        template: `<div>访问的 URL 是: {{ url }}</div>`
    })
    // 我们可以通过传入一个"渲染上下文对象",作为 renderToString 函数的第二个参数,来提供插值数据:
    renderer.renderToString(app, {
        // 页面 title 将会是 "Hello"
        title: 'hello',
        // meta 标签也会注入
        meta: `
          <meta charset="utf-8" />
        `
    }, (err, html) => {
        if (err) {
            res.status(500).end('Internal Server Error')
            return
        }
        res.end(html)
    })
})
server.listen(8080)

为了简化这些,你可以直接在创建 renderer 时提供一个页面模板。多数时候,我们会将页面模板放在特有的文件中,例如 index.template.html

<!DOCTYPE html>
<html>
  <head>
    <!-- 使用双花括号(double-mustache)进行 HTML 转义插值(HTML-escaped interpolation) -->
    <title>{{ title }}</title>

    <!-- 使用三花括号(triple-mustache)进行 HTML 不转义插值(non-HTML-escaped interpolation) -->
    {{{ meta }}}
  </head>
  <body>
    <!--vue-ssr-outlet-->
  </body>
</html>

注意 <!--vue-ssr-outlet--> 注释 -- 这里将是应用程序 HTML 标记注入的地方。

然后,我们可以读取和传输文件到 Vue renderer 中:

生命周期

由于没有动态更新,所有的生命周期钩子函数中,只有 beforeCreate 和 created 会在服务器端渲染 (SSR) 过程中被调用。这就是说任何其他生命周期钩子函数中的代码(例如 beforeMount 或 mounted),只会在客户端执行。

DOM 和 BOM

通用代码不可接受特定平台的 API,因此如果你的代码中,直接使用了像 windowdocument,这种仅浏览器可用的全局变量,则会在 Node.js 中执行时抛出错误,反之也是如此。

避免状态单例

当编写纯客户端 (client-only) 代码时,我们习惯于每次在新的上下文中对代码进行取值。但是,Node.js 服务器是一个长期运行的进程。当我们的代码进入该进程时,它将进行一次取值并留存在内存中。这意味着如果创建一个单例对象,它将在每个传入的请求之间共享。

如基本示例所示,我们为每个请求创建一个新的根 Vue 实例。这与每个用户在自己的浏览器中使用新应用程序的实例类似。如果我们在多个请求之间使用一个共享的实例,很容易导致交叉请求状态污染 (cross-request state pollution)。

因此,我们不应该直接创建一个应用程序实例,而是应该暴露一个可以重复执行的工厂函数,为每个请求创建新的应用程序实例:

const Vue = require('vue')
module.exports = function createApp(context) {
    // 为每个请求创建一个新的根 Vue 实例
    return new Vue({
        data: {
            url: context.url
        },
        template: `<div>访问的 URL 是: {{ url }} </div>`
    })
}

并且我们的服务器代码现在变为:

const fs = require('fs')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer({
    template: fs.readFileSync('./index.template.html', 'utf-8')
})
// 导入封装好server.js
const createApp = require('./app')
server.get('*', (req, res) => {
    const context = { url: req.url }
    // 为每个请求创建新的应用程序实例,避免导致交叉请求状态污染
    const app = createApp(context)
    // 我们可以通过传入一个"渲染上下文对象",作为 renderToString 函数的第二个参数,来提供插值数据:
    renderer.renderToString(app, {
        // 页面 title 将会是 "Hello"
        title: 'hello',
        // meta 标签也会注入
        meta: `
          <meta charset="utf-8" />
        `
    }, (err, html) => {
        if (err) {
            res.status(500).end('Internal Server Error')
            return
        }
        res.end(html)
    })
})
server.listen(8080)

同样的规则也适用于 router、store 和 event bus 实例。你不应该直接从模块导出并将其导入到应用程序中,而是需要在 createApp 中创建一个新的实例,并从根 Vue 实例注入。

使用 webpack 的源码结构

现在我们正在使用 webpack 来处理服务器和客户端的应用程序,大部分源码可以使用通用方式编写,可以使用 webpack 支持的所有功能。同时,在编写通用代码时,有一些事项要牢记在心。

一个基本项目可能像是这样:

src
├── components
│   ├── Foo.vue
│   ├── Bar.vue
│   └── Baz.vue
├── App.vue
├── app.js # 通用 entry(universal entry)
├── entry-client.js # 仅运行于浏览器
└── entry-server.js # 仅运行于服务器

app.js

app.js 是我们应用程序的「通用 entry」。在纯客户端应用程序中,我们将在此文件中创建根 Vue 实例,并直接挂载到 DOM。但是,对于服务器端渲染(SSR),责任转移到纯客户端 entry 文件。app.js 简单地使用 export 导出一个 createApp 函数:

import Vue from 'vue'
import App from './App.vue'

// 导出一个工厂函数,用于创建新的
// 应用程序、router 和 store 实例
export function createApp () {
  const app = new Vue({
    // 根实例简单的渲染应用程序组件。
    render: h => h(App)
  })
  return { app }
}

entry-client.js:

客户端 entry 只需创建应用程序,并且将其挂载到 DOM 中:

import { createApp } from './app'

// 客户端特定引导逻辑……

const { app } = createApp()

// 这里假定 App.vue 模板中根元素具有 `id="app"`
app.$mount('#app')

entry-server.js:

服务器 entry 使用 default export 导出函数,并在每次渲染中重复调用此函数。此时,除了创建和返回应用程序实例之外,它不会做太多事情 - 但是稍后我们将在此执行服务器端路由匹配 (server-side route matching) 和数据预取逻辑 (data pre-fetching logic)。

import { createApp } from './app'

export default context => {
  const { app } = createApp()
  return app
}

参考文档

vue中axios和await/async的使用

安装

安装或者引入CDN文件

npm install axios
<script src="https://unpkg.com/axios/dist/axios.js"></script>
<!--<script src="https://unpkg.com/axios/dist/axios.min.js"></script>-->

GET

在Vue原型链上绑定,就可以全局使用$http方法

import axios from 'axios';
Vue.prototype.$http = axios;

然后我们就可以,其他地方使用的话,如同使用vue-resource一样,我们还可以在get或者post请求后面增加请求头header

this.$http.get("http://www.tuling123.com/openapi/api", {
    params: {
        key: "c75ba576f50ddaa5fd2a87615d144ecf",
        info: "先有鸡还是先有蛋"
    },
    header:{}
}).then((data) => {
    console.log(data);
    //success callback
}, (err) => {
    //error callback
})

POST

post请求比get请求复杂一点,首先降Content-Type格式为application/x-www-form-urlencoded,因为axiospost方法默认使用application/json格式编码数据,那么解决方案就有两种,一是后台改变接收参数的方法,另一种则是将axiospost方法的编码格式修改为application/x-www-form-urlencoded,这样就不需要后台做什么修改了

import axios from 'axios'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';//全局更改
import qs from "qs";//配合qs模块转化post请求的参数,记得先npm install qs
Vue.prototype.$axios = axios;
Vue.prototype.$qs = qs;

然后在组件中这样使用

export default {
    this.$axios({
      method: "post",
      //headers: { "content-type": "application/x-www-form-urlencoded" },//局部更改
      url: "http://localhost:3000/users/test",
      data: this.$qs.stringify({
        name: ""
      })
    }).then(res => {
      console.log(res);
    });
  }
};

具体或者其他方法可以参考官方文档的这篇解决方案using-applicationx-www-form-urlencoded-format

代理

比如在vue-cli3中我们可以这样配置代理来解决跨域问题,在package.jsonbabel.config.js同级目录下新建vue.config.js文件写入以下代码

module.exports = {
    baseUrl: '/',
    devServer: {
        proxy: {
            '/api': {
                target: 'https://m.nubia.com',
                changeOrigin: true,
                ws: true,
                pathRewrite: {
                  '^/api': ''
                }
            }
        }
    }
}

正常情况请求https://m.nubia.com/show/page/phoneType是会跨域的,经过上面配置,就可以用/api/show/page/phoneType代替来访问

this.$axios({
    method: "get",
    url: "/api/show/page/phoneType",
    //    /api/show/page/phoneType代替https://m.nubia.com/show/page/phoneType
}).then(res => {
    console.log(res);
});

二次封装

我们可以对 axios 进行一次二次封装,方便我们做全局的请求拦截或者全局的加载动画,这里我们也需要引入 qs 模块对 post请求的参数进行处理,并且把它命名为 http.js,可以在其他组件引入使用

import axios from 'axios'
import qs from 'qs'

axios.interceptors.request.use(config => {
    // loading 做loading的加载动画
    return config
}, error => {
    return Promise.reject(error)
})

axios.interceptors.response.use(response => {
    return response
}, error => {
    return Promise.resolve(error.response)
})

function checkStatus(response) {
    // loading
    // 如果http状态码正常,则直接返回数据
    if (response && (response.status === 200 || response.status === 304 || response.status === 400)) {

        return response
        // 如果不需要除了data之外的数据,可以直接 return response.data
    }
    // 异常状态下,把错误信息返回去
    return {
        status: -404,
        msg: '网络异常'
    }
}

function checkCode(res) {
    // 如果code异常(这里已经包括网络错误,服务器错误,后端抛出的错误),可以弹出一个错误提示,告诉用户
    if (res.status === -404) {
        alert(res.msg)
    }
    if (res.data && (!res.data.success)) {
        alert(res.data.error_msg)
    }
    return res
}

export default {
    post(data, url) {
        return axios({
            method: 'post',
            url: url,
            data: qs.stringify(data),
            timeout: 10000,
            headers: {
                'X-Requested-With': 'XMLHttpRequest',
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
            }
        }).then(
            (response) => {
                return checkStatus(response)
            }
        )
    },
    get(url, params) {
        return axios({
            method: 'get',
            baseURL: 'https://cnodejs.org/api/v1',
            url,
            params, // get 请求时带的参数
            timeout: 10000,
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        }).then(
            (response) => {
                return checkStatus(response)
            }
        ).then(
            (res) => {
                return checkCode(res)
            }
        )
    }
}

安装async和await支持

webpack自己搭建的脚手架即使按照官网安装好最新的babel,它本身也还是不支持async/await

所有还需要额外安装

npm install --save-dev babel-plugin-transform-runtime
npm install --save babel-runtime // `babel-plugin-transform-runtime` 插件本身其实是依赖于 `babel-runtime` 的,但为了适应 `npm install --production` 强烈建议添加该依赖。

webpack文件夹目录新增一个. babelrc,这份是babel配置文件

然后在里面写入

{
  "plugins": ["transform-runtime", "babel-plugin-transform-regenerator", "babel-plugin-transform-es2015-modules-commonjs"]
}

webpackuse里面的options注释掉

{
    test: /\.js$/,
    // 除了node_modules|bower_components所有的js文件都用babel-loader处理
    exclude: /(node_modules|bower_components)/,
    use: {
        loader: 'babel-loader',
        // options: {
        //     presets: ['@babel/preset-env']
        // }
    }
}

配合awiat和async

引入上面的 http.js 然后在 methods 属性里面定义一个 getData 方法并用 async 关键词,测试发现不加也没有问题,然后就可以在里面对每一个异步请求返回的 promise 请求进行 await 同步队列处理,这样就可以解决很多在 vue 中经常出现的问题,比如回调嵌套或者 this 的指向性问题

关于promise&&await&&deferred和event loop可以参考这里

import http from "../libs/http.js";
export default {
  data() {
    return {
      topics: null,
      topic: null
    };
  },
  methods: {
    async getData() {
      // 两个异步请求变为同步执行,也不需要再
      // 执行顺序是 topics -> -----------(2秒后)  -> topic
      // topics异步
      const topics = await http.get("https://cnodejs.org/api/v1/topics", {
        page: 1,
        tab: "ask",
        limit: 10,
        mdrender: false
      });
      console.log("topics");
      this.topics = topics.data.data;
      // setTimeout异步
      const test = await (() => {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve("-----------");
          }, 2000);
        });
      })();
      console.log(test);
      // topic异步
      const topic = await http.get(
        "https://cnodejs.org/api/v1/topic/5433d5e4e737cbe96dcef312"
      );
      console.log("topic");
      this.topic = topics.data.data;
    }
  },
  mounted() {
    this.getData();
  }
};

参考文档

vue执行ajax请求

代码如下

var demo2 = new Vue({
            el: '#demo2',
            data: {
                a: 1,
            },
            methods: {
                ajax: function() {
                    var self = this;
                    var xmlhttp = new XMLHttpRequest();
                    xmlhttp.onreadystatechange = function() {
                        if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                            var json = JSON.parse(xmlhttp.responseText)
                            console.log(json)
                            console.log(self.a)
                            self.a = json.name;
                        }
                    }
                    xmlhttp.open("GET", "test.php", true);
                    xmlhttp.send();
                }
            }

        })

视图如下

{{a}}
<button @click="ajax()">Ajax</button>

这里要注意的是ajax函数里的this是指向demo2对象的,而xmlhttp.onreadystatechange指向的则是自己,所以这里要用var self = this;保留对demo2对象的指向,然后就可以用原生的JS实现ajax的请求了

php代码如下 test.php

<?php
echo json_encode(array('name'=>'wsscat'));
?>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.