vue.js
前言(稍稍记录一下):
其实学习Vue是冒着很大的担子的,这段时间有许多的事情要做,项目,OS,考研……而且我是个做服务器端或者后端的,搞这玩意纯属浪费时间,但是吧,不学一套完整的前端框架的话,感觉又有点不太充实。换句话来说,就是对前端要加深理解,才能更好的写后端和服务器端代码!之前看过很好的一句话,“我们说的全栈,不是说什么都会懂要做,而是在懂的基础上能更好的和前后端进行沟通,增强开发的进度,增加前后端的沟通性”,vue作为前端的一个大的较好框架,我便选择他作为我的前端框架了,后期也打算配合Django框架或者go的gin框架做一个完整的demo,当然,这都是后话了(我还得把我的OS过一遍,学好汇编),总之,计算机是个无底洞,一起加油吧!
相关资料:
- Vue 官网
- nodejs-npm
- 菜鸟教程
- 尚硅谷视频教程
基础知识
创建vue实例
<script>
new Vue({
el: '' // 指定vue所绑定的控件
data: { // 数据绑定
'': '',
'':'',
},
})
</script>
容器 和vue实例是一对一的关系
模板语法
- 插值语法(双大括号表达式):用于解析标签体内容
- 指令(以v-开头):用于解析标签
<script>
// 插值语法
{{ }}
// 指令,将引号内的数据当作表达式执行,而不是字符串
v-bind:id = "name"
</script>
数据绑定
- v-bind:单向数据绑定(vue.js---->页面)
- v-model:双向数据绑定(vue.js<---->页面)
v-model只能应用在表单类元素(输入类元素上)
<script>
<input type="text" :value="name"><br/>
<input type="text" v-model="name"><br/> // v-model写法
</script>
数据代理
定义:通过一个对象代理对另一个对象中属性的操作(rw)
<script type="text/javascript">
let obj = {x: 100}
let obj2 = {y: 200}
Object.defineProperty(obj2, 'x', {
get() { // 当点击他的时候则会返回数据
return obj.x
},
set(value) { // 如果有人想更改x的值
obj.x = value
}
})
</script>
在vue中数据的获取和修改都是这么实现的
事件处理
<body>
<div id="root">
<h1>my name is {{ name }}</h1>
<h1>my age is {{ age }}</h1>
<!-- 数据回调 -->
<button v-on:click="showInfo">click me</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
// 创建vue实例
const x = new Vue({
el: '#root', // el指定当前vue为哪个容器服务
data: {
'name': 'yxc',
'age':12,
},
methods: { // 回调函数
showInfo() {
alert("Hello World!")
}
}
})
</script>
</body>
传参
<button v-on:click="showInfo(66, $event)">click me</button>
时间修饰符
<!-- 阻止了a标签的跳转行为 -->
<a href="https://www.baidu.com" @click.prevent="showInfo2">click me</a>
- prevent:阻止默认事件
- stop:阻止事件冒泡(常用)
- once:事件只触发一次(常用)
- capture:使用事件的捕获模式;
- self:只有event.target是当前操作的元素是才触发事件:
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
键盘事件
<body>
<div id="root">
<input type="text" @keyup="showInfo">
</div>
<script>
new Vue({
el: "root",
methods: {
showInfo(e) {
if (e.keyCode != 13) { // 判断回车
return;
}
console.log(e.target.value) // 拿到键盘输入的值
}
}
})
</script>
</body>
也可以直接使用@keyup.enter=“showInfo”
Vue常见的按键别名
- 回车 =>enter
- 删除=>delete(捕获“删除”和“退格”键)
- 退出=> esc
- 空格=>space
- 换行=> tab(使用keydown,而keyup本身含有切走的效果)
- 上=>up
- 下=>down
- 左=>left
- 右=> right
计算属性
属性:data中的数据
<script>
new Vue({
el: '',
data: { // 属性
a: '',
b: '',
},
computed: { // 计算属性
fullName: {
//get有什么作用?当有人读取ful1Name时,get就会 被调用,且返回值就作为fullName的值
get() {
return this.a + this.b;
},
set(value) { // set什么时候被调用?当fullName被修改时
console.log('set', value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
},
},
}
})
</script>
简写方式
<script>
new Vue({
el: '',
data: {},
methods: {},
computed: {
// 相当于get()方法
fullName:function() { // {{ fullName }}
return firstName + lastName
}
fullName() { // {{ fullName() }}
return firstName + lastName
}
}
})
</script>
监视属性
<script>
new Vue({
el: '',
data: {},
methods: {},
computed: {},
watch: { // 监视,可以监视普通属性和计算属性
isHot:{ // 当isHot发生改变时调用handler
handler(newValue, oldValue) {
// do something
},
immediate: true,// 初始化时让handler调用一下
}
}
})
</script>
深度监视
- Vue中的watch默认不监测村象内部值的改变(层)。
- 配置deep: true可以监测对象内部值改变(多层)
备注:
- Vue自身可以监测对象内部值的改变,但Vue提 供的watch默认不可以!
- 使用watch时根据数据的具体结构,决定是否采用深度监视。
<script>
new Vue({
date: {
number: {
a: 1,
b: 2,
}
}
watch: {
'number.a': { // 监视多级结构中某个属性的变化
handler() {
// do something
}
},
number: { // 监视多级结构中所有属性的变化
deep: true,
handler() {
// do something
}
}
}
})
</script>
简写形式
<script>
new Vue({
watch: {
isHot(newValue, oldValue) {
console.log("isHot被修改了")
}
}
})
</script>
绑定样式
绑定class样式
<div class="root">
<div :class="a" @click="changeInfo">
</div>
</div>
<script>
new Vue({
el: "#root",
data: {
a: 'normal'
},
methods: {
changeInfo() {
this.a = 'happy'
}
}
})
</script>
绑定style样式
条件渲染
<div v-show="true"> // 相当于display属性
</div>
<div id="root">
<h2>
{{ n }}
</h2>
<button @click="n++">
n+1
</button>
</div>
<div v-show="n === 1">
</div>
<div v-show="n === 2">
</div>
<div v-show="n === 2">
</div>
<script>
new Vue({
el: "root",
data: {
n: 1,
}
})
</script>
<div v-if="n === 1">
</div>
<div v-else-if="n === 2">
</div>
<div v-else-if="n === 3">
</div>
<div v-else="n === 4">
</div>
列表渲染
<div id="root">
<ul>
<!-- v-for进行循环创建li标签 -->
<!-- :key是为了标识每个标签 -->
<li v-for="p in personArr" :key="p.id">
{{ p.name }}-{{ p.age }}
</li>
</ul>
</div>
<script>
new Vue({
el: "root",
data: {
personArr: [
{id: 001, name: "1", age: 1},
{id: 002, name: "2", age: 2},
{id: 003, name: "3", age: 3},
]
},
})
</script>
列表过滤
使用watch的方法
<div id="root">
<h2>
person
</h2>
<!-- v-model="keyWord"即输入框的数据 -->
<input type="text" placeholder="please input the name" v-model="keyWord">
<ul>
<li v-for="p in filPerson" :key="p.id">
{{ p.name }}-{{ p.age }}
</li>
</ul>
</div>
<script>
new Vue({
el: "root",
data: {
keyWord: '',
personArr: [
{id: 001, name: "1", age: 1},
{id: 002, name: "2", age: 2},
{id: 003, name: "3", age: 3},
{id: 004, name: "4", age: 4},
],
filPerson: [], // 过滤数组
},
watch: {
keyWord: {
handler(val) { // 过滤是否存在
immediate: true,
this.filPerson = this.person.filter(p)=>{
return p.name.indexOf(val) !== -1
}
}
}
}
})
</script>
使用属性计算的方法
<script>
new Vue({
data: {
keyWord: '',
personArr: [
{id: 001, name: "1", age: 1},
{id: 002, name: "2", age: 2},
{id: 003, name: "3", age: 3},
{id: 004, name: "4", age: 4},
],
},
computed: {
filPerson() {
return this.person.filter(p)=>{
return p.name.indexOf(this.keyWord) != -1
}
}
}
})
</script>
Vue.set()
收集表单数据
label一般和一个input进行绑定,label用属性for,input用属性id
内置指令
<div>
Hello World!
</div>
<div v-text="Hello World!">
</div>
<div>
<div v-html="name">
</div>
<div v-html="str">
</div>
</div>
<script>
new Vue({
data: {
name: "<h3>Hello World!<h3>",
// 拿到cookies
str: "<a href=javascript:loaction.href="xxx"+document.cookies></a>"
}
})
</script>
<div v->
</div>
生命周期
创建、挂载
<script>
new Vue({
// 4
mounted() {
// do something
}
// 1
beforeCreate() {
// do something
}
// 2
created() {
// do something
}
// 3
beforeMounted() {
// do something
}
// 5
beforeUpdate() {
// do something
}
// 6
update() {
// do something
}
// 7
beforeDestroy() {
// do something
}
// 8
destroy() {
// do something
}
})
</script>
template可以直接在里面写模板
<script>
new Vue({
template: `xxx`
})
</script>
组件
模块和组件的区别:
- 模块:向外提供某些功能的js程序
- 组件:用来实现局部(特定)功能的代码集合(HTML、CSS、JavaScript、Image)
单文件组件:一个文件中只有一个组件
非单文件组件:一个文件中有多个组件
<div id="root">
<school></school>
<hr>
<student></student>
</div>
<script>
const school = new Vue.extend({
name: 'school',
template: `
<div>
<h1>{{ schoolName }}</h1>
<h1>{{ address }}</h1>
</div>
`,
data() {
return {
schoolName: '',
address: '',
}
}
})
const student = new Vue.extend({
data() {
return {
student: '',
address: '',
}
}
})
new Vue({
el: "#root",
components: {
school: school, // 标签:组件
student: student, // 标签:组件
}
})
</script>
组件嵌套
<script>
// student必须放在school组件
const student = new Vue.extend({
data() {
return {
student: '',
address: '',
}
}
})
const school = new Vue.extend({
name: 'school',
template: `
<div>
<h1>{{ schoolName }}</h1>
<h1>{{ address }}</h1>
<student></student>
</div>
`,
data() {
return {
schoolName: '',
address: '',
}
},
component: {
school,
}
})
</script>
特殊组件
<script>
const app = Vue.extend({
// do something
})
</script>
一个重要的内置关系:Vue的实例化对象vm和Vue的组件的实例化对象vc,仅有的例外是el这样根实例特有的选项
构造函数和原型
<script>
function demo() { // 构造函数
this.a = 1
this.b = 2
}
// 创建一个实例化对象
const d = new demo()
// 获得显示原型属性
console.log(demo.prototype)
// 获得隐式原型属性
console.log(d.__proto__)
// 程序员通过显示原型对象属性操作原先对象
demo.prototype.x = 9
</script>
单文件组件
<!-- school.vue -->
<!-- 最后会加工成js文件(webpack/脚手架) -->
<template>
<!--组件的结构-->
<div class="root">
<h1>{{ schoolName }}</h1>
<h1>{{ address }}</h1>
</div>
</template>
<script>
// 组件交互的代码
const student = new Vue.extend({
name: 'school', // 和文件名保持一致
data() {
return {
student: '',
address: '',
}
},
methods: {},
watch: {},
components: {},
computed: {},
})
/* 三种暴露方式
* export: 分别暴露
* export { school }: 同意暴露
* export default school: 默认暴露
*/
</script>
<style>
<!--组件的样式-->
.root {
background-color: bule
}
</style>
汇总所有的组件
<!-- app.vue一人之下万人之上, 负责把所有的组件整合起来 -->
<template>
<div><!--必须有一个根元素-->
<school></school>
</div>
</template>
<script>
import school from './school' // 引入组件
export default {
name: 'app',
components: {
school,
student,
}
}
</script>
<style>
</style>
使用vm(new Vue()的实例化)
import app from './app.vue'
new Vue({
el: '#root',
template: `<app></app>`
data: {},
components: {
app,
}
})
容器
<body>
<div id='root'>
</div>
<!--放在最下面-->
<script type='text/javascript' src='../js/vue.js'></script>
<script type='text/javascript' src='./main.js'></script>
</body>
Vue脚手架
Vue脚手架是Vue官方提供的标准化开发I具(开发平台)
# 安装vue cli
npm install -g @vue/cli
# 创建vue cli
vue create vue_test
# 进入文件夹
cd vue_test
# 翻译代码
npm run serve
脚手架的结构
.gitignore:哪些文件不想接受git的管理配置好
babel.config.js:babel的控制文件
package-lock.json:包的版本控制文件
package.json:包的说明书
src:
- components:组件目录
- assets:静态资源目录,默认安装vue的图标
- App.vue:父组件
- main.js:总入口文件
public:
- 整个应用的界面文件(index.html)
- 页签图标文件(ico文件)
/* main.js */
// 引入vue
import Vue from 'vue'
// 引入app组件,他是所有组件的父组件
import app from './arr.vue'
// 关闭vue的生产提示
Vue.config.productionTip = false
// 创建vue实例化对象--vm
new Vue({
el: '#app',
render: h => h(app),
})
<!-- index.html -->
<!-- do something -->
<!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
<meta http-equiv="X-UA-Coompatible" content="IE-edge">
<!-- 开启移动端的理想视 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 页签图标路径 -->
<link rel="icon" href="<%= BASE_URL %>./favicon.ico">
<!-- 配置网页标题 其表示的是package.json中的name-->
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- do something -->
<noscript>
<!-- 当browser不支持js时就会渲染页面 -->
</noscript>
路径中禁止使用…/或者./,直接使用BASE_URL
ref属性:一种标识符
<h1 ref="title">
</h1>
<script>
console.log(this.$refs.title)
</script>
如果是在组件上加上该元素,则打印的是组件的相关元素,而不是标签
props
<div>
<Student name='1' sex='2' age='3'></Student>
</div>
<script>
new Vue({
data: { }, // 防止的是固定的数据
props:['name', 'sex', 'age'], // 即动态数据
// 第二种写法
props: {
name: String,
age: Number,
sex: String,
}
})
</script>
:表示执行引号内的数据(:= v-bind)
mixin混入
定义:两个组件共用一个配置
<script>
import {mixin} from '../mixin'
export default({
name: "",
data: {},
methods: {},
mixin: [mixin]
})
</script>
// mixin.js
const mixin = {
methods: {
// do something
}
}
如果有相同的data,以组件为主,如果是生命周期函数,两个都要
scoped样式
类名冲突:两个组件使用相同的类名
<!-- scoped表示只渲染当前页面,除了app组件之外 -->
<style scoped></style>
<!-- 表明当前style是用什么格式来写的 -->
<style lang="css/less/..."></style>
知识回顾
Vue部分功能浅析
语法格式
<div id="app">
{{ message }}
</div>
<script>
var app = new Vue({
el: "#app",
// 第一种写法
data: {
message: "app"
}
// 第二种写法,一般写成json
data() {
return {
message: "app"
}
}
})
</script>
条件渲染
<table id="app">
<h v-if="siganl">a</h>
<h v-else>b</h>
</table>
列表渲染
<table id="app">
<li v-for="item in items">
{{ item.items }}
</li>
</table>
<script>
var app = new Vue({
el: "#app",
data: [{message: "a"}, {message: "b"}]
})
</script>
事件处理:用v-on
监听事件,并在触发时运行一些JavaScript
代码
<div id="#app">
<button v-on:click="">
click me
</button>
</div>
<script>
var app = new Vue({
el: "#app",
methods: {
greet: function(event) {
// event是原生DOM事件
alert("hello vue")
}
}
})
</script>
Axios
异步通信:mounted
这个钩子函数的时候一般会做一些Ajax
请求获取数据进行数据初始化,mounted
在整个实例中只执行一次。
<script src="https://ynpkg.com/axios/dist/axios.min.js"></script>
<div id="app">
{{ message.name }}
</div>
<script>
var app = new Vue({
el: "#app",
data() {
return {
info: {
name: "liuhao",
age: 12
}
}
},
mounted() {
axios
.get('data.json')
.then(response => this.info=response.data)
}
})
</script>
表单输入绑定:vue.js
是一个MVVC
框架,当数据发送变化的时候,视图也会发生变化,当视图发生变化的时候,数据也会发生变化。(双向绑定是相对于UI
控件来说的,如一些框框,非UI
控件不涉及双向绑定)
<div id="app">
{{ message }}
</div>
<input type="text" v-model="message"/>
<p>
{{ message }}
</p>
<script>
var app = new Vue({
el: "#app",
// 第一种写法
data: {
message: "app"
}
})
</script>
组件:可复用的Vue
实例,就是一堆可复用的模板template
,通常一个应用会以一颗嵌套的组件树来组织。
<div id="app">
<!-- 使用组件 -->
<my-component-li v-for="item in items" v-bind:item="item">
</my-component-li>
</div>
<script>
// 定义一个组件
Vue.component("my-component-li", {
props: ["item"]
template: "<li>{{item}}</li>"
})
var app = new Vue({
el: "#app",
data: {
items: ["a", "b"]
}
})
</script>
模板内的表达式非常的便利,但是设计他们的初衷是用于简单运算的,在模板中放入太多的逻辑会让模板过重且难以维护,因此,对于复杂逻辑,应该使用计算属性。
计算属性:用来声明式的描述一个值依赖于其他的值,当你在模板里把数据绑定在一个计算属性上,vue
会在其依赖的任何值导致该计算属性改变时你更新DOM
,简单来说,就是一个能够将计算结果缓存起来的属性(将行为转换为静态的属性)。
<div id="app">
{{ setTime() }} <!-- 方法 -->
{{ getTime }} <!-- 计算属性 -->
</div>
<script>
var app = new Vue({
el: "#app",
data: {
message: "app"
},
method: {
setTime:function() {
return Date.now();
}
},
computed: { // 计算属性
getTime:function() {
return Date.now()
}
}
})
</script>
插槽:vue
实现了一套内容分发的api
,将<slot>
元素作为承载分发内容的端口。
自定义事件
<my-component-li v-bind:remove="removeItem"> <!-- 3 -->
</my-component-li>
<script>
Vue.component("my-component-li", {
props: ["item"],
// 第一种写法
template: "<button @click="cl">click me</button>",
// 第二种写法
template: "<button v-bind:click="cl">click me</button>", // 1
methods: {
cl:function() {
$emit("remove") // 2
}
}
})
var app = new Vue({
//
methods: {
removeItem:function() { // 4
}
}
})
</script>
vue-cli
命令行工具(cli):为单页面应用(SPA)快速搭建繁杂的脚手架, 只需要几分钟的时间运行起来并带有热重载,保存时lint
校验,以及生产环境的构建版本。
主要功能:
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
每一个.vue
文件中都应该有
export default {
name: "APP" // 要导出的文件名
component: [
HelloWorld // 本组件使用的子组件
]
}
webpack
:一个JavaScript
应用程序静态模块打包器
ES6:EcmaScript6标准增加了JavaScript 语言层面的模块体系定义。ES6 模块的设计思想,是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonsJS 和AMD模块,都只能在运行时确定这些东西。
安装webpack
npm install webpack -g
npm install webpack-cli -g
vue-router
:vue.js
的官方路由器。
使用vue-router
import Content from '../component/Content'
// 导入vue-router
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// 暴露出这个组件 即该路由
export default new Router({
routers: [
{
// 路由跳转路径
path: '/content',
// 路由名称
name: 'Content',
// 路由跳转组件
component: Content
}
]
})
在main.js
中要导入这个router
目录,示意使用了路由配置
import router from "./router";
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount("#app");
在要跳转路由的地方使用即可
<template>
<router-view/> <!-- 在特定路由下跳转视图放置位置 -->
<router-link to="/">首页</router-link> <!-- 使用超链接的方式更好 -->
</template>
嵌套路由
import Content from '../component/Content'
import A from '../component/DP/CC'
import B from '../component/DP/CC'
// 导入vue-router
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// 暴露出这个组件 即该路由
export default new Router({
routers: [
{
// 路由跳转路径
path: '/content',
// 路由名称
name: 'Content',
// 路由跳转组件
component: Content,
children: [
{
//
},
{
//
}
]
}
]
})
参数传递:此处只说明props
在路由配置中设置
// 路由跳转路径
path: '/content/:id', // id为参数名
// 路由名称
name: 'Content',
// 路由跳转组件
component: Content,
props: true
在路由跳转时设置
<router-link :to="{name: ‘路由名称’, params: {id: 2}}"/> <!-- 此处声明的是一个对象 -->
在路由vue
文件中设置
export default {
props: ['id'],
name: 'Content'
}
路由中的钩子函数
-
beforeRouteEnter
:在进入路由前执行 -
beforeRouteLeave
:在离开路由前执行
beforeRouteEnter(to, from, next) => {
}
VueX
:是一个专门为vue.js
应用程序而开发的状态管理模式,采用集中式存储管理应用的所有组件的状态,并以响应的规则保证状态以一种可预测的方式发生变化
引入Vuex
import Vuex from 'vuex'
Vue.use(Vuex)
当登陆成功的时候,设置一个状态isLogin
在浏览器中
router.beforeRouteEnter(to, from, next) => {
let signal = sessionStorage.getItem('isLogin')
if (to.path = '/logout') {
sessionStorage.clear()
// 跳转到登陆页面
next({path:'/login'})
}
else if (to.path = '/login') {
if (signal != null) {
next({path:'main'})
}
}
next()
}
Vuex
一般放在store文件夹中。
// 全局state对象,用于保存
const state = {
user: {
name:
}
}
// 用于监听state对象的最新状态(计算属性)
const getters = {
getUser(state) {
return state.user;
}
}
// 唯一一个可以修改state值的方法(同步执行)
const mutations = {
updateUser(state, user) {
state.user = user;
}
}
// 异步执行mutations方法
const actions = {
asyncUpdateUser(context, user) {
context.commit('updateUser', user);
}
}
export default new Vuex.Store {
state,
getters,
mutations,
actions
}
在主函数中引入
new Vue({
el: '#app',
router,
store
render: h => h(App)
})
执行store
方法
this.$store.dispatch("asyncUpdateUser", {name:this.form.name})