vue2
一、介绍
- vue 就是一个 js 文件: vue.js
- 官方叫法: 渐进式 javaScript 框架
- 为什么要设计成一个 渐进式的框架?
- vue.js 是一个核心库(相当于毛坯房)
- 可以根据自己需要添加各种功能插件(安装下载):都是依赖于vue.js
- 例如:vue.js + router.js + vuex.js + elementui + xx.js
也就是从 毛坯房 + 家具 + ...... 渐进式地搭建和完善项目
二、脚手架(vue-cli)
2.1 介绍
点击 官网 进行下载安装等工作
测试
vue -v
有相关提示即表示安装成功创建项目:
vue create project_name
然后根据自己的需要选择创建项目启动项目:
cd project_name npm run serve
2.2 脚手架目录结构
vue.config.js: 配置文件
- 可以用来配置脚手架的一些功能,具体看 官方文档
README.md: 项目说明文件
- 项目的介绍说明文档
package.json: 项目的模块依赖和一些项目配置
name、version: 项目名称、版本
scripts: 调试命令,将键名serve改成 aaa,然后就可以用
npm run aaa
实现npm run serve
的效果,一样的,只是改了名称。
node_modules目录: npm下载的模块包存放地
npm install xx
默认下载到当前项目的node_modules文件夹中,属于局部安装,跟跟--save一样npm install xx -s
其中 s 表示 --save,生产环境依赖,在项目中构建项目,是项目的一部分npm install xx -d
其中的 d 表示 --save-dev,开发环境依赖,用于打包、解析代码的,项目上线时并不需要使用到的npm install xx -g
加了 -g 就会放到系统的 node_modules文件夹,成为全局模块。
src 目录:
- main.js: 全局js文件
- App.vue: 第一个入口的 vue 文件
- assets: 存放静态资源,如图片
- components: 存放组件文件
public 目录:
- favicon.ico: 网页图标
- index.html: 项目运行时 http://localhost:8080/ 访问的页面文件,主要是
<div id="app"></div>
这个dom,通过webpack等一系列工具打包编译的js文件会自动加到这个html文件中,从而使vue项目运行起来http://localhost:8080/
访问的是:http://localhost:8080/index.html
,也就是我们vue项目的 public 目录,因此我们可以在浏览器输入http://localhost:8080/favicon.ico
访问到网页图标,但是一般不会这样使用,静态资源一般放服务器上,了解就好
三、Vue 的开发模式
入口: public ==> index.html(main.js) ==> app.vue
旧的开发方式:一个页面有 html\js\css 等文件组成,而操作由js的getElementById等方法获取dom进行一系列的操作
Vue的开发方式:数据驱动模式(数据驱动dom)
- 页面布局、js、css 都在 .vue文件去写
.vue 文件组成(三个部分)
template标签: 写dom盒子,在vue2中,template标签下只能存在一个父元素标签
script标签: 写代码逻辑
style标签: 写样式
vue基本使用
- 在 data函数 中定义数据:
<script>
export default {
data() {
return {
data1: '第一个数据'
}
}
}
</script>
- 在 template 中使用数据
<template>
<div id="app">
<h1>{{ data1 }}</h1>
</div>
</template>
- 在 style 中定义布局样式
<style>
#app {
text-align: center;
color: #2c3e50;
}
</style>
四、vue 部分指令
v-for: 循环指令,一般配合 key 来使用,key值必须唯一
v-for= "item in data" :key='item' v-for= "(item, index) in data" :key='index' ##其中,data可以是数组,也可以是对象。
v-if、v-else-if 、v-else: 判断指令,为真则进行创建渲染,反之则删除
v-if="false" 条件假:直接把元素删除了,或者是没有创建这个元素 v-show="true" 条件真:重新创建一个元素,并渲染页面上面来
v-show: 显示和隐藏指令,原理是使用css的 display:none; 进行控制隐藏
v-show="false" 还存在页面上,但是是一个隐藏元素(标签style属性添加了display:none;) v-show="true" 是一个正常显示元素(正常标签,没有添加style)
面试题: v-show 和 v-if 的区别
v-show 和 v-if 条件都是 false 的时候
v-show 是隐藏(显示和隐藏元素)
v-if 是删除 (v-if是创建和删除盒子)
在首次加载页面(开销问题)时
v-show ==> 就算设置了 false,元素也是存在的,只不过隐藏了
v-if ==> false时是不存在的,当设置为 true 时才创建元素出来
当频繁的切换时
v-show css控制显示和隐藏,开销比较小
v-if 创建和删除,不断地创建和删除元素,开销会大很多
五、vue 定义方法
在 methods 中定义方法func, 在标签中使用 @事件名='func' 进行事件的绑定
在 data函数 中定义数据,在 methods对象 中定义方法,可以使用 this.xx 操作 data 中 xx 数据
<template> <div class="hello"> <h1>{{ data1 }}</h1> <button @click="func">点击</button> </div> </template> <script> export default{ data() { return { data1: 1 } }, methods: { func() { this.data1 += 1 console.log('我是一个方法') } } } </script>
六、vue 数据流
data 函数返回的对象中的属性会成为当前组件的 Vue 实例的响应式数据:
当 Vue 实例创建时,这些data中的属性会被添加到 Vue 实例上作为其属性,并且会在组件的生命周期中被使用
生命周期:
1. beforeCreate: 组件实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用 2. created: 组件示例创建完成后立即调用 3. beforeMount: 在挂载开始之前被调用,相关的 render 函数首次被调用。 4. mounted: el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子函数。 5. beforeUpdate: 数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前 6. updated: 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子函数 7. beforeDestroy: 实例销毁之前调用,在这一步,实例仍然完全可用 8. destroy: 实例销毁后调用。此时,所有的事件监听器已被移除,所有的子实例也已经被销毁
data()、methods、computed等选项中定义的数据和方法,都会被转化为组件实例对象的属性和方法,可以在组件的模板和JavaScript代码中直接使用。
computed: 计算属性
- 针对data数据的二次计算(直接在模板template里面也可以写运算逻辑,但是不建议,会很乱、难以维护,当计算逻辑复杂一点的时候很难写)
<template> <div class="hello"> <h1>总价: {{ total }}</h1> </div> </template> -------------------------------- data() { return { price: 100, num: 1, } }, computed: { total() { return this.price * this.num } }
- 其中,total这个函数,最终会变成当前组件实例的一个属性,跟data里的变量一样,那么这里就有一个问题,如果我total函数是methods里面定义的,其实也可以实现一样的效果,只不过写到模板里面要加total(),运行一下得到返回值
<template> <div class="hello"> <h1>总价: {{ total() }}</h1> <h1>总价: {{ total() }}</h1> <h1>总价: {{ total() }}</h1> </div> </template> -------------------------------- data() { return { price: 100, num: 1, } }, methods: { total() { return this.price * this.num } }
面试题: 注意,这里的最大区别是: computed有缓存, 如上面的代码,我total()写了三个,那么就会执行三次函数,每次都会重新计算,如果使用 computed 定义total,那么total的返回值只要不发生变化,就会一直使用缓存,不会重新计算,提高了性能
前面说过,computed 属性里面的方法会转化为当前组件实例的属性,因此我们是可以通过 this.total 获取到这个计算属性 total 的,但是却不能直接进行修改,如在methods里的方法进行修改
this.toal = '520'
会报错如果需要对计算属性进行修改赋值操作,需要把 total 写成对象写法,定义 get、set 属性方法,在set方法里面进行相应的处理
data() { return { price: 100, num: 1, } }, methods: { total: { get() { return this.price * this.num } set( val ) { this.price = val } } }, methods: { changeTotal() { this.total = 1000 } }
- 注意,在上面的例子中,如果我要直接修改计算属性 total, 需要将total写成对象写法,定义 get,set 属性方法,set(val) 中的参数 val 就是要对total进行赋值的值,在 set(val) 里面,仍然不能
this.total = val
,需要折中处理,它只是可以监听到修改操作,并获取修改的值val参数。可以data再定义一个变量进行存放,这里我不太明白这个set的应用场景。。。应该主要是起到监听操作的效果
属性绑定
单向数据流(v-bind)
vue 是 MVVM 架构的,M(model)为 script 标签的js数据, V(view)为template模板视图, VM(viewModel)为vue框架
v-bind 简写:
v-bind:value ===> :value v-bind:src ===> :src v-bind:class ===> :class 一切属性都可以进行绑定。。。
M(js数据)定义数据 --> v(视图template)使用数据
注意 如果是单向数据流,视图修改了值,M的值不变(不会更新M)
双向数据流(响应式数据:v-model 简写为 @ )
- M(js数据) 可以给 V(template) 使用,当 V修改数据的时候,会同时改变 M 中的数据;当 M 改变数据,V 中显示的数据也会响应式的变化。
单向绑定和双向绑定的使用场景
单向绑定:纯展示类的数据
双向绑定:有修改或者输入等交互行为的,如要进行登录,在输入的时候,输入数据的同时改变了js中的数据,以便获取到需要的参数值
面试题:
双向绑定:v-model
vue中如何实现单向绑定呢?v-bind
七、生命周期
介绍
生命周期是指一个对象从诞生到死亡的一个过程,在vue中就是指一个组件从实例化诞生,到实例销毁的过程,具体看官网
先是 new Vue 实例化
然后创建前后, 挂载前后,数据更新前后,组建销毁前后
生命周期的书写顺序可以随便写,不会影响执行顺序,因为 Vue 源码已经规定好了各个生命周期执行的顺序了。
什么情况下使用哪些生命周期
created ---> 请求接口,初始化时
mounted ---> dom操作的时候
updated ---> 观测数据是否更新了
destroyed ---> 关闭(没了):
- 当用户关闭页面,但是业务上要记录一些东西的时候
- 例如看视频网站时,当播放页播放的视频达到一定时长,用户关闭了播放页,这时候可以在这个生命周期中记录一下时长数据,当用户再次播放这条视频的时候,自动从上一次的时长开始播放。
面试题
this.$data : 当前组件的data数据
this.$el : 当前组件的节点(dom,template模板下的dom)
第一次进入时,执行几个生命周期?
4个 创建前后: beforeCreate: 没有data, 没有el created: data创建了,el为undefined 挂载前后 beforeMount: data,el为undefined(其实已经在准备了) mounted: data,el创建了
因此,在涉及到dom操作的时候,要在 mounted 生命周期及之后进行
八、请求数据
在vue中,一般使用 axios 、fetch 来发起请求
出现跨域问题
设置代理解决:
vue.config.js配置: https://cli.vuejs.org/zh/config/
如果你的前端应用和后端 API你需要在“开发环境”下将 API 请求代理到服务器没有运行在同一个主机上,API 服务器。这个问题可以通过vue.config.js 中的 devServer.proxy选项来配置。
注意: 设置代理在开发环境生效,生产环境不生效!! !
- 在vue.config.js中配置代理来解决跨域的问题
devServer:{
proxy: http://xxx.xxx
}
注意: 设置完代理后,一定要重启一下
axios的二次封装 [公司项目中基本都会做的]
二次封装的意义:当然有很多,目前先知道: 方便统一管理
在src中新建一个目录utils/request.js,然后创建请求实例
api的解耦[公司项目中基本都会做的]
api的解耦的意义:
为了同一个接口可以多次使用,那么封装起来,直接调用就可以了
为了方便api请求统一的管理
请求写在某一个.js文件
- 引入axios示例,然后按照页面分类统一写请求,使用的时候,直接引入并传参数即可
补充 : @ 代表 src目录
注意
不管什么情况,vue的项目: axios二次封装和api解耦是一定会做的
请求接口: 会有2种情况
1.前端不用处理跨域
- 不需要设置代理来解决跨域问题
2.前端需要处理跨域问题
设置代理
通过配置 vue.config.js 来解决,但是只限于开发环境,项目打包之后,代理就失效了
设置环境变量(根据不同的环境来执行不同的值)
创建环境变量文件,在vue项目根目录创建:
开发环境 ----> .env.development 生产环境 ----> .env.production
.env文件中属性名必须以
VUE_APP_
开头调用要重启 vue 项目
调用 .env 文件中的变量: p
rocess.env.VUE_APP_XXX 例如:
console.log(p
rocess.env.VUE_APP_XXX)
$nextTick获取更新后的dom ( onload 完成后获取)
想要获取更新后的dom : this.$nextTick
在 beforeCreate 、createdbeforeMount想要获取dom,可以使用$nextTick,传入一个函数,函数逻辑内可以获取到dom元素
beforeCreate() {
this.$nextTick(() => {
console.log('获取dom', document.getElementById('xxx'));
})
}
ref , 获取dom
- 直接在元素标签上写属性 ref='xx',然后这个属性就会添加到组件实例的属性上,可以通过
this.$refs.xx
获取到对应的dom
<template>
<div ref="box">123</div>
</template>
<script>
export default{
mounted() {
console.log(this.$refs.box);
console.log(this.$refs['box']);
}
}
- ref 拿到的是一个dom对象,可以通过
this.$refs.xx
或者this.$refs['xx']
拿到
九、组件
概念
就是把一个大的网页,拆分成多个模块,由多个模块组成一个大网页
就是一个 .vue 文件
管理
统一放到src/components里面, 可以新建成一个个文件夹,注意引入路径就好
组件的命名: 首字母要大写
引入子组件,在父组件的script标签下:
// 引入和注册 <script> import Xxx from '@/components/Xxx' export default { components: { xxx, } } </script> // 使用组件 <template> <div> <Xxx></Xxx> <Xxx /> </div> </template>
父组件传值给子组件
父组件如何传值
死数据:
<Child msg="父组件给子的jjjbbbb"></Child>
活数据:
<Child :msg="msg"></Child>
, 其中,msg是父组件的一个变量,而且需要使用 v-bind 进行单向绑定, 因此不可以进行修改,会报错。注意:如果父组件传的是 数组 或者 对象,是可以修改的,但是不建议修改。
子组件如何接收
数组形式: 直接与data()函数同级写
props: ['msg']
对象形式(限定类型和默认值等):
props: { msg: { type: String, default: '默认值1' } }
通过 props 接收的数据, 跟data数据一样会被添加为子组件实例属性,因此可以通过
this.msg
进行获取
子组件传值给父组件
父组件定义事件并传递给子组件
<template> <Child @sendMsg="sendMsg"></Child> </template> <script> import Child from '@/components/Child' export default{ components: { Child }, methods: { sendMsg(msg) { console.log('父组件接收:', msg); } } } </script>
子组件通过
this.$emit('事件', '参数1',...)
触发父组件的事件,从而将子组件的信息通过参数形式进行传递,父组件中的事件就可以处理子组件的参数信息。
注意 父组件传递的事件是自定义事件名称 xxo,函数为 xxoo,那就通过 @xx0='xxoo'
进行绑定传递,子组件要通过 $emit('xxo') 这个事件名称进行触发。
兄弟组件之间传值(新建一个vue实例)
新建工具文件 bus.js
兄弟组件之间的传值可以通过一个 bus.js 来进行管理, bus.js 内容:
import Vue from 'vue' export default new Vue()
就是新建一个 vue 实例就可以了,然后需要引入才能使用
A 兄弟组件触发自定义函数并传递参数
import bus from '@/utils/bus' <button @click="JJJ">兄弟</button> methods: { JJJ: function() { bus.$emit('busjj', this.data2) } }
B 兄弟组件监听自定义时间并接收参数 (可以写在任意一个生命周期内,只要执行了就能绑定事件, 因此不要放到methods方法里面,不然需要先触发方法才能绑定事件)
import bus from '@/utils/bus' bus.$on('busjj', ( val ) => { console.log('接收:', val) })
注意:
- 这个 bus.js 通过导出一个 vue 实例来作为事件总线,本身已经脱离了组件那种依赖关系,因此不管是父子、兄弟、爷孙组件等都可以进行通信,只要引入了就可以进行事件的绑定和触发。