前端框架基础 vue01
前端开发的变化过程
1、html js css 导入
2、构建工具(编译工具 webpack,vite2) + 新语言(ES6,typescript) + 前端框架(vue,react,agular,flutter…)(数据驱动视图、路由机制)
xxx.vue
3、脚手架 CLI
4、SPA: single page application
5、MPA : multiple page application
6、模块化 node require Import
.js css 都会被模块化
二、VUE CLI 使用
-
主流版本 cli2 cli3.x != vue版本
安装
安装完成后,就可以使用vue指令来创建和管理一个脚手架项目
npm i -g vue-cli // 2.x npm i -g @vue/cli // 默认安装最新版 ( 一般不安装最新版本 , 防止插件不匹配) npm i -g @vue/cli@4.x //安装4.x八本 npm uninstall -g vue-cli //删除 npm uninstall -g @vue/cli
npm i 模块名@版本号
一般在安装命令行(cli)模块时 , 我们采用 -g 全局安装 , 为的是保证安装出来的命令行指令在系统的每个目录都可以使用 , 相当于配置了该指令的"环境变量"
##### 创建项目
*先在cmd中进入要创建项目目录,然后再执行以下指令* cli3.x
```shell
vue create + 项目名
vue ui 项目的可视化管理工具, 类似于 phpmyadmin
Cil2的创建方式(老版本)
vue init webpack 项目名称
三.项目架构说明
-
public目录
该项目中, 存放得是 htmlwebpackPlugin的模板文件 index.html
该目录也是devserver的开发服务根目录
-
src 目录
项目代码目录 : 所有业务代码都在改目录中
- main.js : webpac打包入口文件 , 所有代码都从该文件开始执行
import Vue from 'vue'; import App from 'App.vue'; //创建了 vue 实例 new Vue({ render: h => h(App) // 将App.vue中的结构渲染到#app上 }).$mount('#app') // 该vue是以模板文件index.html , div#app作为挂载点
- App.vue : 挂载到 div#app中的母版 , 这里面一般只放一个标签
<template> <div class="app"> <router-view></router-view> </div> </template>
- assets : 静态资源目录 ( 本地所使用一些静态文件,js,css,图片,字体 )
- img
- font
- css
- js
- 剩余的目录都可以根据工程需要自行创建 原则上是 : 按照同一类型用同一文件夹
例如 : views 放所有页面vue
页面架构
(放图片)
-
.vue页面基本构成
- 页面vue命名规范 , 大驼峰命名法
- Home.vue
html部分 <template> <div>必须有div包裹</div> </template> // js部分 <script> </script> <style scoped> /* 当前页面的样式部分 */ 样式隔离 //scoped 属性添加后 , 编译生成的样式会带有一个随机的hash属性选择器 , 用于隔离区分不同 //vue 组件中的css选择器 //效果如下 .box[data-v-fae5bece] { color: tomato; } </style>
-
基本的路由配置
vue+webpack做的是单页面的SPA应用 , 因此内部页面跳转 , 本质上是标签容器的切换 , 这种切换需要利用前端的路由概念实现 , 因此我们需要安装一个别人已经写好的路由跳转插件 vue-router
//main.js
import RouterVue from 'router-vue' //导入
//挂载到vue原型
vue.use(RouterVue);
//创建路由对象
const router = new RouterVue({
//配置路由表
routes:[
{
path:'/home',
componet:() => import('@/views/Home.vue')
},
{
path:'/work',
componet:() => import('@/views/Work.vue')
}
]
})
//注入vue实例
new vue({
router
})
-
.editorconfig(编辑器配置文件 , 用于设置编辑器的编写特性)
charset = utf-8 // 文件编码格式 style_indent = tab //缩进方式 indend_size= 2 //缩进大小 //vscode必须焊装对应的 , .editorconfig插件才能生效以上配置
-
.geiignore
-
balel-config.js balel 配置文件
-
vue,config.js
// vue.config.js是webpack配置的一个包装后的配置对象文件 module.exports = { lintOnSave : false // 变比eslint检查 chainWebpack : config =>{ //可以通过config来访问配置原始的webpack配置对象 config .resolve .alias .set('v' , path.resolve(__dirame) , './src/views') } }
三、cli目录说明 ()
-
public ==>项目导出文件夹。存放模板html文件
-
src ===> 代码目录
main.js ==>项目入口文件, main.js
App.vue ==> 根组件。 页面上显示的所有内容,都在app.vue中,可以暂时理解成 iframe
components ==>文件组件文件 .vue
.vue文件
<template> <div> </div> </tmplate> <script> export defalut { } </script> <style scoped> /* //scoped 限制style中的类型名不会重命名。各个.vue互不影响 在编译时就会在标签行间添加自定义属性,随机字符串,用于区分不同的.box .box[fasfdsafsdf]{} .box[dasfasa12]{} <div class="box" fasfdsafsdf></div> <div class="box" dasfasa12></div> */ </style>
组件导入
<template> <hw/> </template> <script> import helloworld from "./components/helloworld.vue"; //注册 export defalut { components : { "hw" : helloworld } } </script>
assets :静态资源文件存放处
本地的logo,icon,公共css,公共js
vue.config.js:包装后webpack的配置文件(如果没有可以自己创建)
遵循common.js模块系统
module.exports = {
key : value //配置
}
数据驱动视图
-
MustAche语法 模板语法
<template> <div> {{city}} 北京 {{city + "ddd"}} 北京dd {{city == "北京" ? "111" : "222"}} 111 {{表达式}} <div class="box" :data-id="city"></div> data-id="北京" </div> </template> <script> export default { data(){ return { city : "北京" } } } </script>
-
写在标签行间外 {{表达式}}
<div>{{表达式}}</div>
-
写在标签行间属性内 :属性名=“表达式”
<div :src="表达式" :data-id="表达式"></div>
-
默认都是渲染的是文本,如果渲染html字符串 v-html=“标签字符串变量”
<div v-html="标签字符串变量"></div>
-
-
计算属性(computed)
计算属性常常用在,要被渲染的变量是需要经过复杂的数据处理过程后才显示结果的场景。写在 computed : {},以函数的形式的进行定义。 函数名,就是计算属性名。函数的返回值就是计算属性的值
<template> <div>{{con}}</div> //由于content变量的处理,需要有一个复杂过程(需要多个表达式处理),因此,不能直接给在{{}}中写,此时需要计算属性con </template> <script> export default{ data(){ return { content : "你TMD的可以G了" } }, computed: { con(){ return this.content.replace(/TMD|G/g,argu=> "*".repeat(argu.length)) }, aa : function(){ //可以传参的计算后属性 return (char)=>{ return this.content.replace(/TMD|G/g,argu => char.repeat(argu.length)); } } } } </script>
-
条件渲染
v-if v-else v-else-if
根据不同的表达式结果,来显示不同的组件:特点是如果条件为假,组件都不会被创建
```vue
<template>
<h1 v-if="num > 90">优秀</h1> //num = 200 显示优秀
<h2 v-else-if="num > 80">良好</h2> //num 80-90 显示良好
<h2 v-else-if="num > 60">及格</h2> // num 60-80 显示及格
<h2 v-else>挂科</h2> // num < 60 显示挂科
</template>
<script>
export defalut {
data(){
return {
num : 200
}
}
}
</script>
```
- v-show : 写在组件行间,如果表达式的结果为真,就给该组件设置display:block 如果条件表达式结果为false就给该组件设置display:none
v-show与v-if的区别,v-if条件为假不会创建组件,v-show总会创建组件,因此加载过程v-if要比v-show要快,运行过程,v-if条件变化,需要重新创建DOM,因此执行速度没有v-show快,一般v-if总是用于多显一状态 v-show用于单个盒子显示和隐藏
-
列表渲染
遍历数组
<template> <div v-for="(item,index) in pds" :key="item.id" class="cell"> <h1>{{index + 1}}</h1> <h2>id:{{item.id}}</h2> <h2>书名:{{item.name}}</h2> <h2>作者:{{item.author}}</h2> </div> </template> <script> export default{ data(){ return { pds : [ { id : "001", name : "三国演义", author : "罗贯中" }, { id : "002", name : "红楼梦", author : "曹雪芹" }, { id : "003", name : "西游记", author : "吴承恩" }, { id : "004", name : "水浒传", author : "施耐庵" } ], } } } </script>
遍历对象
<template>
<div v-for="(value,key) in person" :key="key">
{{key}} : {{value}}
</div>
</template>
<script>
export default{
data(){
return {
person : {
name : "张三",
age : 12
}
}
}
}
</script>
遍历数值
<template>
<div class="box" v-for="n in 10">{{n}}</div>
</template>
VUE基础02
手动安装 vue-router默认安装出来的版本是4.x版本, 是为了vue3.0匹配的 , 导致2.0vue核心项目无法继续使用
Failed to compile with 1 error 09:50:56
error in ./src/views/List.vue
Syntax Error: Error: ENOENT: no such file or directory, open 'C:\Users\Administrator\Desktop\09_VUE\lesson_vue_02\vuebase02\src\views\List.vue'
解决方案 : 重装低版本的 vue-router
npm un vue-router
npm i vue-router@3.x --save
采用构建编译的方式开发项目 , 经常会遇到依赖与依赖之间的版本匹配的问题 , 特征是代码不报错 , 项目报错出在依赖中 , 解决方案是尝试不同的依赖版本 , 一般是降低版本
列表渲染
-
普通渲染
- item是数组元素 , 命名可以是任意 , 底层代码是数组遍历内部函数形参
- i 是数组元素下标
- key 属性用于表示当前列表元素的唯一性, 一般动态传入数组下标 或者元素iD
<div class="item" v-for="(item , i) in listData" :key="i"> {{item}} </div> <script> export default { name: "List", data() { return { listData: [ "111111111111111111", "222222222222222222", "333333333333333333", "444444444444444444", "555555555555555555", "666666666666666666", ], index : 1, people : { name : '赵凯', age : 10 }, prolist, prolist1 }; }, methods : { fn(item){ alert(item) } } }; </script>
-
固定次数渲染
<button v-for="i in 5" :key="i">{{i}}</button>
- i是从一开始
- 5表示重复渲染多少次
-
复合数据渲染
<div class="item" v-for="item in prolist" :key="item.id">
<h1>{{item.id}}</h1>
<p>{{item.name}}</p>
</div>
<script>
export default {
data(){
return{
prolist : [
{ id : "001" , "name" : '球' },
{ id : "001" , "name" : '球' },
{ id : "001" , "name" : '球' },
]
}
}
}
</script>
:key=“item.id” 原因是这次遍历没有放入下标i,因此需要从中找一个不重复的属性代替,一般选i
- 嵌套渲染
<template>
<div>
<h1>列表渲染v-for</h1>
<!-- 渲染集合数据 -->
<!-- -->
<div class="wrap4">
<div class="item" v-for="item in prolist1" :key="item.id" :data-id="item.id">
<h1 @click="item.name='哈哈'">{{ item.name }}</h1>
<p>{{ item.price }}</p>
<p>{{ item.url }}</p>
<div class="comments">
//由于co是普通数据 所以需要加上i 写作(co , i)
<span v-for="(co,i) in item.comment" :key="i">
 
{{co}}
</span>
</div>
</div>
</div>
</div>
</template>
<script>
const prolist = [
{
name : '网球',
price : 123 ,
url : 'http://',
comment : ['不错']
},
{
name : '网球',
price : 123 ,
url : 'http://',
comment : ['不错' , '还行']
},
{
name : '网球',
price : 123 ,
url : 'http://',
comment : ['不错' , '还行' , '不咋地']
},
{
name : '网球',
price : 123 ,
url : 'http://',
comment :['不错' , '还行' , '不咋地' , '还可以']
},
]
export default {
name: "List",
data() {
return {
prolist1
};
},
methods : {
fn(item){
alert(item)
}
}
};
</script>
<style scoped>
.active{
background: tomato;
color:blue;
}
</style>
- 列表选中的行操作
- 跟v-for 一同写入行间的指令 , 均表示每行都适用该指令
<div @click="fn(i)" v-for=" i in 5 "></div>
//效果 : 生成五行div , 点击哪行,调用fn(行号)
样式操作
<div :class="{active : index == i}" v-for="i in 5"></div>
一、处理事件
- 事件绑定 (行间绑定)
- v-on:event=“事件处理函数/表达式”
- @event=“事件处理函数/表达式”
- methods:{}
- 修饰符(鼠标,键盘,系统)
- .stop
- .prevent
- .self
- .once
- 事件对象($event)
二、数据双向绑定
-
表单 v-module=“状态量” 接收是 表单的 value
-
input texteara checkbox
三、数据侦听器
-
分离业务 交互–》修改状态==》修改视图
-
数据类型:基本数据, string number bool
-
简写方式, 状态名,作为函数名 这个函数会在状态发生变化的时候被回调
引用类型: Array Object
’obj.属性名‘:function(){}
开启深度监听:
obj:{
handler(){
},
deep : true
}
-
四、组件生命周期
组件,页面跳转也是靠组件的切换完成的:所谓组件的生周期,就是指一个组件在页面上从 开始显示–到消失的过程(创建到销毁的过程)
- beforecreate : 只有 vm = new Vue 功能扩展
- created : 数据监听建立,event 发请求
- beforemount : 建立了虚拟DOM
- mounted : 组件显示出来
+ 原生DOM 组件行间添加一个 ref=“xxx”
+ this.$refs.xxx - beforeupadte( state===>view),不要在里面改变会影响视图变化的状态
- upadted
- beforedestroy
- destroied
Vue 03
计算属性
计算属性主要是用于对各个状态之间进行运算 , 数据过滤的一种处理
<input v-model="name">
<input v-model="version">
<h1>{{ name + version }}</h1>//运算结果直接拼接
<h1>{{ getFullAppVersionName() }}</h1> //函数
<h1>{{ fullAppVersionName }}</h1>//计算属性
<script>
export default {
data(){
return {
name:"",
version:""
}
},
methods:{
getFullAppVersionName(){
return this.name + this.version
},
fn(){
//方法内部可以直接通过this.访问计算属性
console.log(this.fullAppVersionName)
}
},
computed:{
fullAppVersionName(){
return this.name + this.version
}
}
}
</script>
计算属性 VS 方法&视图表达式
计算属性是具有数据缓存的, 当其依赖的状态没有发生变化时 , 计算属性是不会调用求值 的 ,
方法& 视图表达式 ,每次都会进行求值 , 并更新视图
计算属性的完整写法 :
<input v-model="name">
<input v-model="version">
<input v-model="fullAppVersionName">
<script>
export default {
data(){
return {
name:"",
version:""
}
}
computed:{
fullAppVersionName:{
get(){
//计算属性被读取时调用
return this.name + this.version
},
set(){
//计算属性被赋值时调用
this.name = val.split('v')[0];
this.version = val.split('v')[1]
}
}
}
}
</script>
watch 数据监听
允许 我们对任意一个已经注册过的状态 , 进行监听 , 如果状态值发生变化 , 可以进行回调
<div>价格{{ price }}</div>
<input type="text" v-model.number.lazy="count" placeholder="数据监听">
<p>总价{{ totalPrice }}</p>
<script>
export default {
name: "Watch",
data() {
return {
price : 123,
count : 0,
totalPrice : 0
};
},
watch : {
count(){
//当count值发生变化时, 该方法就会被回调
this.totalPrice = this.price * this.count;
},
price(){
this.totalPrice = this.price * this.count;
}
},
}
</script>
watch VS computed
-
共同点 : 都是基于状态变化 , 而触发的一个函数的回调
-
区别
- watch 是一个单纯的监听回调函数 , 因此内部可以写异步操作 , 而computed是利用函数的返回值,作为计算属性的只能是同步过程,
<script> export default{ data(){ return{ count : 10 } }, watch:{ count(){ setTimeout(()=>{ this.count++ },3000) } }, computed:{ count(){ //这样写是无效的 setTimeout(()=>{ this.count++ },3000) } } } </script>
- watch 一次是只能监听一个值的变化 , 计算属性任何一个状态变化 , 都会触发运算 ,
- 面对复杂的多状态运算结果的情况下 , 使用计算属性代码更简洁
<span>价格:<input v-model.number.lazy="price"> </span> <input v-model.number.lazy="count"> <span>总价格:{{ totalPrice }} </span> <script> export default{ data(){ return{ price :123, count :0, totalPrice :0, } }, watch :{ count(){ //当count值发生变化时,该方法会被回调 this.totalPrice = this.price * this.count } price(){ //当price值发生变化时,该方法会被回调 this.totalPrice = this.price * this.count } }, computed :{ //计算属性更简洁 cTotalPrice(){ return this.price * this.count } } } </script>
-
对于多状态变化且同步的预算 , 使用计算属性
-
对于单状态 ., 或监听状态变化后有异步操作业务的 买使用watch
watch完整写法 & 深度监听
export default {
name: "Watch",
data() {
return {
obj : {
name : '赵凯',
age : 12
}
};
},
watch : {
//监听 obj的name 属性
"obj.name" ; {
handler(newVal , oldVal){
//newVal 是变化后的值
//oldVal 是变化前的值
}
},
//直接监听对象
obj : {
handler( newVal , oldVal ){
},
deep:true // 开启深度监听
}
},
}
Vue04 组件
组件基本介绍
组件本质上就是一个对于html,css, js进行的封装,达到能够复用的目的,核心底层就是通过JS面向对象,一个组件就是一个构造方法,本质上就是函数
组件特性:输入、输出
原生方式实现了navItem组件
//定义部分
function navItem(selector, src, title,callback) {
this.el = $(selector); //获取容器元素
this.src = src;
this.title = title;
this.navClick = callback
}
navItem.prototype.initNavItem = function () {
let template = `
<div class="navItem">
<img src="${ this.src }">
<div class="title">${ this.title }</div>
</div>
<style>
.navItem{
width : 150px;
}
.navItem img{
width:100%;
display : "block"
}
.title{
font-size : 18px;
text-align : center;
color : #ccc
}
</style>
`
$(template).appendTo(this.el);
var that = this;
this.el.find('.navItem').click(function(){
that.navClick('数据1',"数据2")
})
}
调用
<nav-item></nav-item>
<script>
var oNav = new navItem('nav-Item');
oNav.src = "../assets/logo.png";
oNav.title = "Vue组件";
oNav.navClick = function(argv){
console.log(argv);
}
//进行初始化
oNav.initNavItem();
</script>
vue中定义组件
-
创建一个.vue文件,一般放在components文件夹下,可以通过webpack配置为componets目录设定新标识
-
定义组件(结构)及样式
<template>
<div class="navItem" @click="fn">
<img :src="src" alt="" />
<slot></slot>
<div class="title">{{ title }}</div>
</div>
</template>
- 对外的普通属性用props:用来接受调用组件时动态传入的数据,可以理解为函数的形式参数
export default {
props : ['src','title'], //形参列表
}
- 对外的回调方法参数
export default{
methods : {
fn(){
this.$emit('navClick','参数1','参数2')
}
}
}
-
插槽 slot
组件之间时互相可以进行组合的,因此组件定义内部插槽相当于一个占位符,用来接受,组件在调用时,写在组件一对标签中的内容
<!--组件插槽定义--> <div class="navItem"> <img :src="src"> <slot></slot> <!--插槽 : 替换成 <h1>哈哈</h1>--> <div>{{ title }}</div> </div> <!--组件调用--> <nav-item> <h1>哈哈</h1> </nav-item>
vue组件调用
- 导入到使用的页面中
import NavItem from 'c/NavItem.vue'
- 在页面中进行局部的注册
export default {
components : {
NavItem
}
}
- 在页面模版中调用
<nav-item
src="" 传递普通参数
:src="" 传递动态变量参数
title=""
@navClick="" 事件要加@
>></nav-item>
组件深入
-
组件的注册
-
局部注册
-
全局注册 需要将注册代码引入到main.js
- 先导入后注册
import NavItem from 'c/NavItem.vue' Vue.component(NavItem.name, NavItem);
- 注册时动态导入
-
Vue.component('NavItem', () => import('c/NavItem.vue'))
-
Propos属性
-
基本定义方法
{ props : ['user','pass','children'] //通过数组方式定义 }
-
props验证定义方法
组件的props相当于函数的形式参数,所以在定义组件时,可以限定参数的类型,以及可以设置参数的默认值,那么这些组件被调用时,系统会根据定义的限定,来对传入的数据进行验证,如果传入数据和定义数据要求不一致,会在控制台以警告的形式进行提示
{ props : { 'user' : String , //单纯限定时字符串 'pass' : [String, Number], //限定可以传入 字符串或密码 'children' : { type : Array , //限定是数组 default : () => [], //引用类型需要用返回值 required : true //是否必传项 }, 'age' : { type : Number, default : 18 , //普通类型,直接写 } } }
-
关于对props传入数据的修改
-
传入的普通数据,number, string ,boolean这些数据都不能直接修改,需要在组件内部 通过computed, watch 监听来进行间接修改
eg:
{ props : ['type'], methods : { changeType(){ this.type = "success" // 这样做是不可以的 } } }
需要用以下的方式
- 通过 data属性暂存: 一般用于传入数据不变量场景
{ props : ['type'], data(){ type1 : this.type //暂存,但是只初始化一次,当外部传入的type发生变化时, type1是不会变的 }, methods : { changeType(){ this.type1 = "success" // 可以修改 } } } <el :type="aa"></el>
- 通过计算属性,来动态监测传入的数据
{ props : ['type'], data(){ type1 : this.type //暂存,但是只初始化一次,当外部传入的type发生变化时, type1是不会变的 }, computed : { type1(){ return this.type + "ddddd" } } }
- 通过watch来监听传入的数据变化,进行间接修改
{ props : ['type'], data(){ type1 : this.type //暂存,但是只初始化一次,当外部传入的type发生变化时, type1是不会变的 }, methods : { changeType(){ this.type1 = "success" // 可以修改 } } watch : { type(){ this.type1 = this.type; } } }
-
如果传入的是引用类型的数据,那么不需要暂存,可以直接修改,原因传入的引用值是不变
{ props : { data : { type : Object, default : () => { return { count : 0 } } } }, methods : { changeType(){ this.data.count++; //没有问题的 } } }
-
vue05 其他
插槽
默认插槽
组件:
<template>
<div class="LoButton">
<button :class="type">
<!-- 定义组件时 , 在组件结构内部用 slot标签来接受 , 组件在调用时 , 写在一堆组件标签中的内容称为slot插槽 -->
<slot></slot>
</button>
</div>
</template>
<script>
export default {
props : ["type"]
}
</script>
父页面调用
<lo-button type="primary"> 登录 </lo-button>
<lo-button type="primary"> 注册 </lo-button>
<!-- 1、默认插槽:标签结构,获取其他组件 -->
<lo-button type="primary">
<input type="text" />
</lo-button>
<!-- 默认插槽:插槽默认值 -->
<lo-button type="primary"> 组件 </lo-button>
<lo-button type="danger"> 张三 </lo-button>
<script>
import LoButton from 'c/LoButton.vue'
export default {
components: {
LoButton,
},
}
}
</script>
具名插槽
组件 :
<template>
<div class="LoNavBar" :style="`width${width}px`">
<div class="leftText">
<div class="back" v-if="isBack">返回</div>
<div class="text" v-else>
<!-- 希望将城市信息放在这个位置 -->
<slot name="leftContent">{{ leftText }}</slot>
</div>
</div>
<div class="title">
<!-- 希望将搜索框放在这个位置 默认插槽 -->
<!-- <slot name="default"></slot> -->
<slot>{{ title }}</slot>
</div>
<!--
可以将传入的props放在slot中作为备用值,插槽默认内容,以实现props和slot的二选一效果
-->
<div class="rightText">
<!-- 希望将img图片放在这个位值 -->
<slot name="rightContent">{{ rightText }}</slot>
</div>
</div>
</template>
<script>
export default {
props: ['title', 'leftText', 'rightText', 'isBack', 'width']
}
</script>
父级调用
<lo-nav-bar
class="nav"
:isBack="true"
leftText="西安"
title="首页"
rightText="蓝鸥"
></lo-nav-bar>
<!-- 正常方式 -->
<lo-nav-bar class="nav">
<template v-slot:leftContent>
<lo-button type="danger">西安</lo-button>
</template>
<input type="text" placeholder="搜索" />
<template v-slot:rightContent>
<img :src="require('@/assets/logo.png')" alt="" />
</template>
</lo-nav-bar>
<!-- 简写方式 -->
<lo-nav-bar class="nav" leftText="北京">
<template #leftContent>
<lo-button type="danger">西安</lo-button>
</template>
<input type="text" placeholder="搜索" />
<template #rightContent>
<img :src="require('@/assets/logo.png')" alt="" />
</template>
</lo-nav-bar>
<script>
import LoNavBar from 'c/LoNavBar.vue'
export default {
components: {
LoButton,
LoNavBar,
LoScope
},
}
}
</script>
作用域插槽
在组件的调用页面,通过<template #插槽名=“插槽对象”>来获取插槽上的自定义的属性值,用于读取组件内的数据
组件
<template>
<div class="scope">
<h1>
<!-- 定义除了name以外的其他 自定义插槽 -->
<slot :user="user" pass="略略"> {{ user.username }} </slot>
</h1>
<!-- 一般会将组件内部的数据 , 放在插槽属性上 , 用于在父组件页面上显示 -->
<slot name="flag" data_id="1001001100"></slot>
</div>
</template>
<script>
export default {
data () {
return {
user: {
nick: '样样都',
username: '从单位车',
hurl: 'cdcwcwqec'
}
}
}
}
</script>
父级调用
<lo-scope>
<template #default="slotScope">
{{ slotScope.user.nick }}
{{ slotScope.pass }}
</template>
<template #flag="slot1Scope">
{{ slot1Scope.data_id }}<!-- 被调用组件的内部数据-->
{{ user.nick }}<!-- 父页面的数据-->
</template>
</lo-scope>
import LoScope from 'c/LoScope.vue'
export default {
components: {
LoScope
},
data () {
return {
name: '张三',
user: {
nick: '达咩',
username: '32423'
}
}
}
}
</script>
过渡
开发过程中的动画 : 专场动画 , 盒子的出现于消失 (组件的出现与消失
- 把要进行动态显示隐藏的组件用系统组件transiton套起来
<transition name="fade">
<lo-nav-bar v-if="isShow">
<template #leftContent>
<h5>西安</h5>
</template>
<h4>首页</h4>
<template #rightContent>
<h5>></h5>
</template>
</lo-nav-bar>
</transition>
- 列表中的元素增加或者减少 列表过渡,使用transition-group组件,其余与transition是一样的
<div class="list">
<transition-group name="fade">
<button v-for="(item , i) in data" :key="i">{{ item }}</button>
</transition-group>
</div>
<button @click="add">add</button>
<button @click="data.pop()">remove</button>
- 设置样式
/* Enter ==> 组件从无到有的过程控制 */
/* name-enter : 定义进入过渡开始状态 */
.fade-enter {
opacity: 0;
transform: translate(-100vw);
}
/* name-enter-active : 定义过渡过程的状态 */
/* 定义过渡时间 过渡时间曲线 */
.fade-enter-active {
transition: 1s linear;
}
/* name-enter-to : 定义过渡的结束状态 */
.fade-enter-to{
opacity: 1;
transform: translateX(0) ;
animation: rotate 2s;
}
/* leave ===>组件从显示到删除的过程控制 */
/* 离开的初始状态 */
.fade-leave {
opacity: 1;
}
/* 离开的过程控制 name-leave-active
过渡时间 过渡时间曲线
*/
.fade-leave-active {
transition:1s linear;
}
/* 离开的结束状态 */
.fade-leave-to {
opacity: 0;
}
@keyframes rotate {
0%{
transform: rotate(0deg);
}
100%{
transform: rotate(360deg);
}
}
指令
局部指令
全局指令
过滤器
Vue06 vue-router
-
安装
npm i vue-router@3.x --save //默认不带指定版本号,装的是4.0,4.0是vue3的专属路由模块
-
配置
//导入vue-router import VueRouter from 'vue-router'; import Vue from 'vue'; //挂载 Vue.use(VueRouter) //创建router实例 const router = new VueRouter({ //配置路由列表 routes:[] }) //注入vue组件 new Vue({ router })
-
实际开发会对路由的配置,做一些目录拆分
-
创建router文件夹,存放路由配置文件index.js,和路由列表文件routes.js
-
路由列表配置文件router/routes.js:用来配置路由列表
export default[];//导出路由列表对象
-
路由配置文件router/index.js:用来进行路由实例的创建配置和导出
//导入vue-router import VueRouter from 'vue-router'; import Vue from 'vue'; //导入路由配置文件 import routes from './routes'; //挂载 Vue.use(VueRouter) //创建router实例 const router = new VueRouter({ //配置路由列表 routes:[] }) //导出路由对象 export default router;
-
main.js导入路由对象
import router from '@/router' //注入 new Vue({ router })
-
-
router-view:路由切换容器
-
Router-link:相当于原生a标签
<router-link to="/path"></router-link>
-
编程式导航:利用this.$router.push()方法
<div @click="$router.push('/path')"></div> //等效于location.hash="path" <script> export default{ methods:{ go(){ this.$router.push('/path') } } } </script>
-
跳转传递参数方法一,发送数据
this.$router.push(path:“”,query:{})
<div @click="go"></div> <script> export default{ methods:{ go(){ this.$router.push({ path:'/path', query:{ name:"张三", age:12 } }) } } } </script> <!-- 等效于原生 --> <div οnclick="location.hash='path?name=张三&age=12'"></div>
-
跳转传参方式二:通过this.$router.push({name:“”,params:{}})
通过这种方式,可以传递任何数据
-
路由标中设置name属性(命名路由)
export default{ { path:"/", name:"home", component:()=>import() } } //向name页面跳转 this.$router.push({ name:"home", params:{ data:[{},{},{}] } })
-
目标页面接收
<div>{{ $route.params.data }}</div>//通过params传参 <div>{{ $route.query.data }}</div>//通过query传参 methods:{ fn(){ this.$route.params.data this.$route.query.data } }
-
-
跳转传参方式三:动态路由匹配
-
配置路由列表
export default{ { path:"/home/:参数key1/:参数key2", name:"home", component:()=>import() } //调用传参 this.$router.push('/home/参数value1/参数value2'); //目标界面通过params接收 <div>{{$route.params.参数key1}}</div> <div>{{$route.params.参数key2}}</div>
-
-
跳转传参方式四:通过组件属性跳转传参
-
路由表配置
export default{ { path:"/home", name:"home", component:()=>import(), props:true, props:{ props:"value1", props:"value2" }, props:to=>{ //to 该路由组件的路由配置对象 return{ props1:"value1", props2:"value2" } } } //调用跳转传参 this.$router.push({ name:"home", params:{ props1:"value",//传递的参数key是目标组件的属性props props2:"value" } }) //目标页面接收 <div>{{ props1 }}</div> <div>{{ props2 }}</div>
-
-
嵌套路由
-
命名视图
-
路由别名:相当于给路由起了一个新名字
{ path:"/page1", alias:"/",//给路由起别名 通过/和/page1都可访问 name:"page1",//命名路由 component: () => import('v/01_page1.vue') },
-
路由重定向:redirect,相当于给路由对象,创建了一个代理对象
export default{ { path:"/page1" }, { path:"/xx", redirect:to=>{ //在实际跳转前干点什么事 return "/page1" } } }
-
路由守卫:路由钩子
- 全局前置 beforeEach(重点)
- 全局后置 afterEach
- 全局解析 beforeResolve
- 路由独享守卫 写在路由配置中 beforeEnter(重点)
- 组件内路由前置守卫 beforeRouteEnter
- 组件内路由离开钩子 beforeRouterLeave (重点)
- 组件内路由更新守卫 beforeRouteUpdate (重点)
-
路由元信息 meta
是给每个路由开辟了一个自定义数据的接口位置,让我们可以个性化定制业务逻辑,一般与路由钩子相互配置,全局路由守卫
export default[ { path:"/path", meta:{ title:'标题', isLoginAuth:true } } ]
-
路由页面滚动保持
业务要求:1、如果是普通跳转,页面回到各个组件的顶部;2、如果是点击的“前进”,“后退”,希望保持页面高度
const router = new VueRouter({ routes, scrollBehavior(to,from,savedPosition){ //如果点击的前进后退,sp是页面滚动条的位置对象{x, y} // 如果是直接跳转,sp是null if(savedPosition){ return savedPosition; } return {x:0,y:0} } })
利用路由守卫实现权限验证
-
不可见(v-if来实现跟权限来显示不同的业务按钮)
-
组件不可访问(通过路由守卫(全局,独享,组件内)实现组件的访问限制,以达到权限验证的目的)
vue07 状态管理
一、状态(state)
我们将可以影响页面视图发生更新的数据,称为状态(data,props,computed,Vue.set vm.$set).
状态管理就是指,如何在各种情况下(父子组件,子子组件,异步数据请求)实现这些状态的 读取和修改
二、Bus(状态管理,适合小型结构简单)
1、创建bus并记载到Vue.prototype。
在main.js
Vue.prototype.$bus = new Vue();
在任意的两个组件内部
A组件(绑定事件监听)
mounted(){
this.$bus.$on('自定义事件名01',callback)
}
B组件(修改状态,触发事件,发送状态)
事件处理函数,或者watch函数中调用
this.$bus.$emit('自定义事件名01',发送状态变量/状态数据)
特别注意:这个数据的传递是单项的,上面只能从B->A ,如果需要A->B,需要再定义一组 自定义事件
三、状态管理Vuex
3-1使用步骤
安装
cnpm i vuex --save
引入:store文件下index.js
import Vuex from 'vuex';
import Vue from 'vue'
挂载Vue原型
Vue.use(Vuex)
创建store实例
const store = Vuex.store({
state : {}, //存放共享数据
getters : {}, //间接读取state共享数据
mutations: {}, //同步修改 state中的数据
actions: {}, //异步修改 state中的数据
moudles : {} //模块化划分
})
将store注入到vm实例
new Vue({
store
})
3-2 state
state中的数据,在组件中是通过计算属性(computed)获取的
-
直接在计算属性中获取
state = { appName : "精英吧" } computed : { appName(){ return this.$store.state.appName //直接获取 } }
当在在组件中需要获取多个state时,可以通过mapState进行展开
-
mapState
import { mapState } from 'vuex'; /* 1、mapState([]) 可以接收一个数组参数,数组元素是 state中的数据key字符串,该函数返回对象,该对象就是定义好的计算属性, 2、通过...展开运算符,可以将返回的计算属性展开,达到简化编写目的 */ computed : { ...mapState(['appName']) //第二种书写方法 ...mapState({ //页面中的计算属性名 从store中获取的状态值 appName : (state) => state.appName }) }
3-3 getters
也是在计算属性中读取,主要用于对state数据的间接处理(间接计算),getters中返回数据是依赖于state的,可以看作是state的一个计算属性
在gtters对象中定义方法
const getters = {
appInfo : state => {
return `${state.appName} - ${state.appVersion} - ${state.appDes}`
}
}
在目标组件中
computed:{
appInfo(){
return this.$store.getters.appInfo
}
}
- mapGetters 展开
{{appInfo}}
{ mapGetters } from 'vuex';
computed : {
...mapGetters(['appInfo'])
}
3-4 mutations
修改state中的数据
-
在非严格模式试下
new Vuex.Store({ strict : false // 非严格模式 })
-
直接修改(但不推荐)
this.$store.sate.appName = "嘿嘿"
-
严格模式下,要通过mutations中定义函数间接修改
const state = { appName : "口袋精英" } const mutations = { SET_APP_NAME : (state,param)=>{ state.appName = param } } //在组件中通过this.$store.commit('mutation函数名',param)修改 'SET_APP_NAME' : (param){ this.$store.commit('SET_APP_NAME' , param); } changeAppName(){ this.SET_APP_NAME("口袋妖怪") }
-
通过mapMutations展开
在组件内
```js
import { mapMutations } from 'vuex';
methods : {
…mapMutations([‘SET_APP_NAME’])
//展开一组函方法
//方法名:
SET_APP_NAME: (param){
this.$store.commit(‘SET_APP_NAME’,param)
},
//使用
changeAppName(){
this.SET_APP_NAME(‘口袋妖怪’);
}
}
```
通过mutation只能修改同步数据: 修改就响应,不能修改异步数据:例如,如果点击按钮后,延迟5s再修改state数据,不允许再mutation中写异步数据处理(文件读取,延迟定时器,网络请求)
3-5 actions
功能与mutation是一样的,可以通过里面定义方法来修改state,mutation中不能有异步事件。而actions没有该限制,一般我们把需要通过异步操作后修改state的代码,写在actions中
const state = {
appName : "口袋妖怪"
}
const mutations = {
SET_APP_NAME : (state,param) => state.appName = param;
}
// 通常在actions中使用异步函数 async函数,来实现异步执行同步化
const delay = time => new Promise((resolve,reject)=>{
setTimeout(resolve,time)
})
const actions = {
//常规函数
after_5_modify : ({commit},param) => {
// 执行异步操作
setTimeOut(()=>{
commit('SET_APP_NAME',param)
},5000)
},
//异步函数
async asyncAfter5Modify({commit},param){
await delay(5000);
commit('SET_APP_NAME',param);
}
}
//组件调用
import { mapActions } from 'vuex';
methods : {
...mapActions(['after_5_modify']),
changeAppName(){
this.after_5_modify('新的值') //5s后更新新的值
}
}
3-6 模块化 分模块
-
使用步骤 定义,注册
-
定义模块 : 创建.js
//子 store user export default { state : {}, mutations : {}, actions : {} getters:{} }
-
注册:
import user from '...user.js' //创建根store const store = Vuex.Store({ modules : { user } })
-
-
state访问 mapState
computed: { ...mapState('user',['sss']), // this.$store.sate.user.sss ...mapState('user',{ sss1 : state => state.sss }) }
-
getters访问 mapGetter
computed: { ...mapGetters('user',['sss']), // this.$store.sate.user.sss ...mapGetters('user',{ sss1 : state => state.sss }) }
-
mutations 使用 mapMutations
methods: { ...mapMutations('user',['SET_SSS']), // this.$store.sate.user.sss }
-
actions 使用mapActions
methods: { ...mapActions('user',['asycnChangeSSS']), // this.$store.sate.user.sss }
3-7 数据持久化
原理: 监听mutations的提交行为,在监听中,不断的将修改后的state存入一个持久化存储区(localstorage /sessionStorage),每次初始化时,先从持久化存储,读取state,将读取的state,替换成当前的state
-
vuex 的插件定义 plugins
//就是一个函数 function saveInLocal(store){ }
-
插件的注册方法
//导入插件 import saveInLocal from '....saveInLocal.js' const store = new Vuex.Store({ plugins : [saveInLocal] })
-
数据持久化写法
//就是一个函数 function saveInLocal(store){ //初始化 if(sessionStorage.state) { store.repalceState(JSON.parse(sessionStorage.state)); } //监听mutation提交动作 store.subscrible((mutation,sate)=>{ sessionStorage.state = JSON.stringfy(state) }) }
Vue08工程化
1.目录框架怎么搭
- 静态资源
- 页面
- 组件
- 路由
- 状态管理
- 库
- 接口管理
- 自定义指令
- 过滤器
- webpack配置
2.网络请求的跨域怎么处理(代理服务)
请求代理
devServer: {
//简写方式 : false,
proxy: {
'/qqApi': {
target: 'https://c.y.qq.com',
pathRewrite: { '^/qqApi': '' }
},
'/jokeApi': {
target: 'https://api.apiopen.top',
pathRewrite: { '^/jokeApi': '' }
}
}
},
//公共URl请求配置
httpConfig:
export default {
baseURL: '',
responseType: 'json',
timeout: 1000,
headers: {}
}
quertList
export const songListApi = () =>{
return req1.request({
url: '/qqApi/v8/fcg-bin/fcg_v8_toplist_cp.fcg?g_tk=5381&uin=0&format=json&inCharset=utf-8&outCharset=utf-8¬ice=0&platform=h5&needNewCode=1&tpl=3&page=detail&type=top&topid=27'
})
}
3敏捷开发(前后端分离) 前端方法 , 模拟接口
MockJS库 , 生成假数据 , 配置请求接口
- 注册接口
- 假数据生成规则
4.Sass css预编译处理器 less
- Sass css预编译处理器 less
- 1.安装
5.组件缓存
6.如何使用原生的库在vue echarts
重写购物车
产品列表
购车列表
选中列表
总价
state