官网 https://cn.vuejs.org/
前言
封装 VS 库 VS 框架
封装通常指一小部分通用业务逻辑,多个封装形成一个模块或者文件,多个模块或者文件就发展成为库或者框架,而插件是为库或者框架发布后做后期补充,可以有官网或者第三方提供的,有点外挂的意思,有时候一个模块就是一个文件,有时候一个文件里面有多个模块,把不同的文件按类别放置到不同的目录里,这个目录叫做包,框架改变了编码思想,库只是个工具你用或者不用不会影响你的编码思想
编码思想
jquery你用或者不用,你的编码思想都会是面向事件,开发一款插件中间用到什么库不重要,你会面向对象开发,如果用vue你要做的就是面向数据
使用vue动态渲染数据
<div id="app">
{{ message }}
div>
引入vue.js之后
var app = new Vue({
// el:'选择器'
el:'#app',
data:{
message:"Hello Vue!"
}
})
注意:多vue实例控制多片html,多实例不可嵌套,body和html不可作为被控制的dom根
vue 是一种配置型框架
初始化数据位置,元数据,是vue实例的一个实例属性,所接受的数据类型,number/string/boolean/array/json/undefined/null
注意:undefined和null不被渲染
插值表达式
{{数据名}} mustache语法 声明式渲染
指令
v-text="数据名"
v-html="数据"
非转义输出
属性
v-bind:html属性="数据"
属性值动态化
:html属性="数据"
简写
v-bind:[属性名]="数据"
属性名动态化
<div id="app">
<h2>{{ text }}h2>
<div v-text="num">
div>
<img :src="url" :[msg]="alt">
div>
let app = new Vue({
el:'#app',
data:{
text:"一些文本",
num:2333,
url:"https://cn.vuejs.org/images/logo.svg",
msg:"alt",
alt:"vue的图片"
}
})
把数据指定到一些dom中去渲染,推荐操作的数据类型:变量数组、对象、字符、数字
<li v-for="值 in 数据">{{值}}li>
<li v-for="值 of 数据">{{值}}li>
<li v-for="(值,索引) in 数组">{{值}}/{{索引}}li>
<li v-for="(对象,索引) in|of 数组">{{对象.key}}/{{索引}}li>
<li v-for="(值,键) in 对象">
<li v-for="(数,索引) in 数字">
<li v-for="(单字符,索引) in 字符">
空数组,null,undefined不循环
找不到的数据就不渲染
<div id="app">
<ul>
<li v-for="data in arr">{{data}}li>
ul>
<ul>
<li v-for="data of arr">{{data}}li>
ul>
<ul>
<li v-for="(data,index) in arr">{{ data }} / {{ index }}li>
ul>
<ul>
<li v-for="(object,index) in arr2">{{ object.id}} / {{ index }}li>
ul>
<ul>
<li v-for="(value,index) in arr3">{{ value.name }} / {{ index }}
<ul>
<li v-for="(value,key) in value.children">{{ value.name }} / {{ key }}li>
ul>
li>
ul>
div>
let app = new Vue({
el: "#app",
data: {
arr: ['aa', 'bb', 'cc'],
arr2: [{
id: 1,
name: 'alex',
age: 18
},
{
id: 2,
name: 'alex2',
age: 28
},
{
id: 3,
name: 'alex3',
age: 38
},
],
arr3: [{
id: 1,
name: 'alex',
age: 18
},
{
id: 2,
name: 'alex2',
age: 28,
children: [{
id: 1,
name: 'ppp',
age: 18
},
{
id: 2,
name: 'ppp2',
age: 28
},
{
id: 3,
name: 'ppp3',
age: 38
},
]
},
{
id: 3,
name: 'alex3',
age: 38
},
],
json: {
a: 1,
b: 2
}
}
})
一段dom可以根据数据有条件的渲染,使用指令v-show
,或者v-if
,对应的值是布尔
<div v-show="true">box1div>
<div v-if="false">box2div>
v-show
与 v-if
的区别
v-show=“布尔” | v-if=“布尔” | |
---|---|---|
区别 | 操作css | 操作dom |
场景 | 适合频繁切换 | 适合不频繁切换 |
性能消耗 | 初始渲染消耗 | 频繁切换消耗 |
<div id="app">
<div v-if="station1">1div>
<div v-else>3div>
<div v-else-if="station2">2div>
<div v-show="station4">4div>
div>
let app = new Vue({
el:"#app",
data:{
station1:false,
station2:true,
station4:false,
}
})
vue通过v-on
指令绑定事件,处理函数需要丢到一个methods
选项当中去
事件名 不带on
new Vue({
methods:{
方法:function(ev,参数){业务}
方法2(ev,参数){业务}
}
})
ev 事件对象,参数可以有多个
注意:vue提供的选项的值如果是函数时,不可用箭头函数 , 会造成this丢失
<div id="app">
<div v-show="station">
一些内容,和显示隐藏关联
div>
<div>{{ arr }}div>
<button v-on:click="show">显示隐藏button>
<button @click="add('d')">新增button>
<button @click="show1">看看箭头函数的thisbutton>
div>
let app = new Vue({
el:"#app",
data:{
station:true,
arr:["a","b","c"]
},
methods:{
// 这是vue虚拟的事件对象
show:function(eve){
// 不传参数,默认传递事件对象
// console.log(eve);
// console.log(this);
// 当前点击的状态
this.station = !this.station
},
add:function(val){
this.arr.push(val);
},
show1:()=>{
// 箭头函数没有this,指向上一层的this,顶层的对象是window
console.log(this)
console.log(app);
}
}
})
视图控制数据,数据也可控制视图,可通过属性+事件绑定实现,也可以使用系统指令v-model
,这个指令可以用在能生产数据的表单元素上
<div id="app">
<input type="text" v-model="value">
<input type="text" :value="value" @input="inputMsg">
<div>{{value}}div>
div>
let app = new Vue({
el: "#app",
data: {
value: "test"
},
methods: {
inputMsg:function(eve) {
// 获取当前输入框的值再赋值给值变量
// v层修改m层,m层通过动态响应再重新渲染v
this.value = eve.target.value
}
}
})
Vue 是个类,所以Vue.protoname
,是类属性|静态属性,Vue.methodname()
是类方法|静态方法,new Vue返回的是实例,所以vm.$protoname
是实例属性,vm.$methodname()
是实例方法,同时vue类内部的this指向的是实例vm,实例vm.$protoname
对等vue选项的key
情况要发生
问题还是要解决
Vue.set(数组, index, value)
vm|this.$set(对象, key, value)
this.$forceUpdate() 强制刷新
this|vm.$mount(’#app’)
注意
不要修改数组的根键,不要修改数组的长度,数据一开始都要声明在data选项内部,不要对数组使用非变异的api
key
key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法
<div id="app">
<ul>
<li v-for="(val,index) in personList" :key="val.id">
{{val.name}}
<button @click="del(index)">删除button>
<button @click="change">改颜色button>
li>
ul>
div>
let app = new Vue({
el: "#app",
data: {
personList: [{
id: "1",
name: "test01"
},
{
id: "2",
name: "test02"
}
]
},
methods: {
del(index) {
this.personList.splice(index, 1)
},
// 事件对象是默认传的
change(eve) {
// 误操作,操作了虚拟dom
// 虚拟dom会拷贝相同的dom,渲染的时候再次渲染不会重新创建
eve.target.parentNode.style.background = "pink"
}
}
})
Vue.js 提供了完全的 JavaScript 表达式支持。这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式
插入数据的地方,可以出现表达式,但不是语句,如{{数据+表达式}}
v-指令="数据+表达式"
<div id="app">
<div>{{ num*10 }}div>
<div>{{ show? "yes" : "no"}}div>
<div>{{ str.split(" ").reverse().join(" ") }}div>
div>
let app = new Vue({
el: "#app",
data: {
num: 10,
show: false,
str: "Hello Vue !"
}
})
是一个函数,所依赖的元数据变化时,会再次执行,平时会缓存,是响应式的,需要在模板中渲染才可调用
计算属性是基于响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数
语法
//定义
computed:{
计算属性: function(){return 返回值}
}
//使用
使用: {{计算属性}} | v-指令="计算属性" | :[计算属性]="计算属性"
computed VS method
method | computed |
---|---|
方法会每次调用 | 基于它们的响应式依赖进行缓存的 |
一般 | 性能高 |
{{methodname()}} | {{computedname}} |
适合强制执行和渲染 | 适合做筛选 |
<div id="app">
<div>{{ "使用计算属性处理逻辑: " + reversMsg }}div>
<h2>用计算属性做筛选h2>
<input type="text" v-model="intMsg">
<ul>
<li v-for="(val,index) of msgArrL">{{ val }}li>
ul>
div>
let app = new Vue({
el: "#app",
data: {
msg: "Just Do It !",
msgList: ["abc", "bcd", "ccc", "dbr"],
intMsg: ""
},
// 计算属性
computed: {
// 翻转字符
reversMsg: function () {
return this.msg.split(" ").reverse().join(" ");
},
// 筛选字符
// 方法简写
msgArrL() {
let result = [];
// 遍历原数据
this.msgList.forEach((val, index) => {
// 查询输入框输入的数据
if (val.includes(this.intMsg)) {
// 把数据添加到空数组中,最后将结果返回出去
result.push(val);
}
});
return result;
}
}
})
需要在数据变化时执行异步或开销较大的操作时,而计算属性是同步的,这个时候需要属性检测watch
定义一个选项
watch:{
数据名:'method函数名' //数据名==data的key
数据名:函数体(new,old){}
数据名:{
handler:fn(new,old){},
deep: true //深度检测
immediate: true //首次运行
}
}
注意:在变更 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。
计算属性 VS 函数 VS 属性检测
计算属性 | 函数 | 属性检测 | |
---|---|---|---|
依赖模板调用且有返回值 | √ | - | × |
是否缓存 | √ | × | √ |
异步 | × | √ | √ |
<div id="app">
<input type="text" v-model="input">
<ul>
<li v-for="(val,index) in list">{{ val }}li>
ul>
div>
let app = new Vue({
el: "#app",
data: {
list: [],
input:''
},
watch: {
input() {
setTimeout(() => {
let result = ['a', 'b', 'c']
this.list = result;
}, 1000)
}
}
});
操作样式,就是属性绑定,只不过绑定的属性是class和style
绑定形式
属性值的类型支持
字符/对象 / 数组
示例
.Fblue{
color: blue;
}
<div id="app">
<h1 :class="blue">class绑定h1>
<h1 :style="yellow">style绑定h1>
<h1 :style="[{background:'green'},{'font-size':'12px'}]">属性值类型支持:字符/对象/数组h1>
div>
let app = new Vue({
el:"#app",
data:{
blue:"Fblue",
yellow:"background-color: yellow;"
}
});
扩展了html语法功能,区别了普通的html属性,vue系统自带了指令,也可自定义指令来扩展,所有系统指令在官方文档的API处提供
v-pre
保留字不编译,原样输出,跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译
v-cloak
防闪烁,模板没编译完,电脑配置差,有可能会看到{{}},体验不佳,用css先隐藏,之后再显示,包括被包裹的子元素
v-once
只渲染元素一次。随后的重新渲染被忽略,包括被包裹的子元素。这可以用于优化更新性能
<div id="app">
<h3>系统指令h3>
<div>{{msg}}div>
<div v-text="msg">div>
<div>{{s}}div>
<div v-html="s">div>
<div>
<h3>div事例h3>
<pre>
<div>内容div>
function show(){
...
}
pre>
div>
<h3>mustatch语法h3>
<div v-pre>{{内容}}div>
<div v-once>{{msg}}div>
<div v-cloak>
<div>
{{12}}
<ul>
<li><a href="">{{'qq'}}a>li>
ul>
div>
div>
div>
let vm = new Vue({
el: '#app',
data: {
msg: '数据1',
s: '强壮'
},
})
系统指令在不够用的情况下,考虑自定义,指令是个函数|对象,用来操作dom的, 里面的this 返回window
全局定义
Vue.directive('指令名',函数(el,binding){})
局部定义
new Vue({
directives:{
指令名 : function(el,binding){},//简写方式: bind + update
指令名(el,binding){},
指令名:{
inserted:fn(el,binding){} //绑定指令的元素插入到父节点时调用 v-focus
bind:fn //指令第一次绑定到元素时调用 v-drag
update:fn //指令所在的元素的model层的数据,view有更新请求时
componentUpdated:fn //更新完成时
}
}
})
<div id="app">
<h1 v-color>自定义全局指令默认值h1>
<h1 v-color="'pink'">自定义全局指令指定值h1>
div>
<div id="app1">
<h1 v-color>自定义局部指令默认值h1>
<h1 v-color="'yellow'">自定义全局指令指定值h1>
div>
// 全局指令
// el:使用指令的dom元素 binding:对象,含有调用指令时传入的参数
Vue.directive("color", (el, binding) => {
// 默认值设为蓝色
el.style.background = binding.value || "blue";
});
// 局部指令的优先级比全局的优先级高
// 局部指令
let app1 = new Vue({
el: "#app1",
directives: {
color: (el, binding) => {
// 默认值设为红色
el.style.background = binding.value || "red";
}
}
});
// 要选中dom才能使用vue渲染
let app = new Vue({
el: "#app",
});
参考文档
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
实例:获取输入框焦点
<div id="app">
<h3>自定义指令-钩子h3>
<input type="text" name="" id="">
<input type="text" v-focus>
<input type="text" name="" id="">
div>
// 原生获取焦点的写法
// window.οnlοad=function(){
// let oIpt = document.getElementById('ipt');
// oIpt.οnclick=function(){}//鼠标触发
// oIpt.click();//模拟点击
// oIpt.focus()
// }
let vm = new Vue({
el: '#app',
data: {
msg1: '数据1'
},
directives: {
// 没有加钩子
/* focus(el,binding){
// console.log(1,el,binding,this);// this指向window
el.focus()
} */
// 钩子函数可选
focus: {
// inserted函数:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
inserted(el, binding) {
// console.log(1,el,binding,this);// this指向window
el.focus()
}
}
}
})
向服务器发送ajax请求,抓取数据
function maxios(options) {
return new Promise((resolve, reject) => {
//-1 整理options
options = options || {};
options.data = options.data || {};
options.timeout = options.timeout || 0;
options.method = options.method || 'get';
//0 整理data
var arr = [];
for (var key in options.data) {
arr.push(key + '=' + encodeURIComponent(options.data[key]));
}
var str = arr.join('&');
//1 创建ajax对象
if (window.XMLHttpRequest) {
var oAjax = new XMLHttpRequest(); //[object XMLHttpRequest]
} else {
var oAjax = new ActiveXObject('Microsoft.XMLHTTP')
}
if (options.method == 'get') {
//2.
oAjax.open('get', options.url + '?' + str, true);
//3.
oAjax.send();
} else {
//2.
oAjax.open('post', options.url, true);
//设置请求头
oAjax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
//拼接好的地址栏
oAjax.send(str);
}
//3.5 超时
if (options.timeout) {
var timer = setTimeout(function() {
alert('超时了');
oAjax.abort(); //中断ajax请求
}, options.timeout);
}
//4.
oAjax.onreadystatechange = function() { //当准备状态改变时
if (oAjax.readyState == 4) { //成功失败都会有4
clearTimeout(timer);
if (oAjax.status >= 200 && oAjax.status < 300 || oAjax.status == 304) {
resolve(JSON.parse(oAjax.responseText))
} else {
reject(oAjax.status)
}
}
};
})
}
this.$http|axios({配置}).then(成功回调(res)).catch(失败回调(res))
this.$http|axios.get(url,{配置}).then(成功回调(res)).catch(失败回调(res))
this.$http|axios.post(url,data,{配置}).then(成功回调(res)).catch(失败回调(res))
配置:
url:“地址“
method: “ 提交姿势”
params:{} 地址栏携带的数据
data:{} 非地址栏携带数据res: 响应体 数据是在res.data内部 vue-resource 返回的数据在res.body内部
// axios({ //第三方的axios
this.$http({ //把axios绑定到Vue的原型
url: './data/user.json'
}).then(
res => console.log('maxios res', res)
).catch(
err => console.log('maxios err', err)
)
axios({
url: 'http://localhost:80/php7/get.php',
params: {//地址栏数据
a: 11,
b: 22
}
}).then(
res => this.list = res.data
)
前端和后端的工程文件不在同一个域,也会出现跨域,以下是解决方案
部分接口允许
//node 要允许的接口内部
res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
//php端
header('Access-Control-Allow-Origin:*');
所有接口允许
//node端
let cors = require('cors');
app.use(cors({
//允许所有前端域名
"origin": ["http://localhost:8001","http://localhost:5000","http://localhost:8080"],
"credentials":true,//允许携带凭证
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE", //被允许的提交方式
"allowedHeaders":['Content-Type','Authorization','token']//被允许的post方式的请求头
}));
jsonp
浏览器装插件 正向代理
开发环境做代理(webpack,反向代理,客户端代理)
1.搭建php环境
2.链接mysql数据库
3.测试接口(根据接口文档测试)
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会
官方文档
这个流程图就是vue整个的生命周期流程,我们能操作的是红色虚线箭头指向的阶段
根据这个流程图我们可以有两次绑定元素的机会:el和template,如果在这两个参数都不绑定的话html中的vue将不被选中渲染
最推荐的是在mounted中添加自己的代码,因为mounted已经将vue渲染完毕,如果进行修改的话vue的响应式系统会响应;
最不推荐的是在updated中添加自己的代码,在这里添加代码如果操作变化的数据会引起响应式系统一直在响应变化的数据,即一直在Mounted渲染,不会停止。
组件是可复用的 Vue 实例,且带有一个名字
Vue根实例表示1个应用,一个应用有若干个组件拼装而成
使用组件
<组件名>组件名>
<组件名/>
定义组件
let 组件变量名= Vue.extend({
template:'我是header组件' //组件使用的模板
});
let 组件变量名={ template: 对应的模板的id }; //√
注册组件
//全局注册
Vue.component('组件名',组件变量名);
//局部注册
components:{ //选项
组件名:组件变量名 //√
}
案例
<div id="app">
<v-head>v-head>
<v-footer>v-footer>
div>
<template id="tHead">
<div class="Head">headdiv>
template>
<script type="x-template" id="tFoot">
<div class="footer">footer</div>
script>
// 定义组件
let VHead = {
// 用template绑定相应的外部模板
template: "#tHead"
}
let VFoot = {
template: "#tFoot"
}
// 注册组件(将组件名和组件变量名关联)
// 全局注册
Vue.component("v-head", VHead)
let app = new Vue({
el: "#app",
// 局部注册
components: {
"v-footer": VFoot
}
});
组件数据data
一个组件的 data 选项必须是一个函数,且要有返回object,因此每个实例可以维护一份被返回对象的独立的拷贝,否则组件复用时,数据相互影响。这是因为js本身的特性带来的,跟vue本身设计无关,也就是说组件的作用域是独立的
组件模板
外部模板,字符模板(行间模板|inline-template)
//=========外部模板========
//模板定义1
<template id="id名">
<div>...div>
tempalte>
//模板定义2
<script type="x-template" id='id名'>script>
<script>
let 组件变量名= {
template:'#id名' //组件使用的外部模板
};
script>
//=========字符模板========
<script>
let 组件变量名= Vue.extend({
template:'...'
});
script>
组件的注意事项
组件名不可和html同名
组件没有el选项,只有根实例存在el
组件的模板有且只有一个根元素
组件的data是个函数,需要返回对象
书写风格
单文件组件
xx.vue,内部组成(script + template + style)
案例
<div id="app">
<v-head>v-head>
<v-foot>v-foot>
div>
<template id="vhead">
<div class="vhead">
<img :src="url" alt=""/>
<ul>
<li v-for="(val,index) in List">{{ val }}li>
ul>
div>
template>
<style>
.vhead{
background-color: cadetblue;
}
style>
<!-- 外部模板 -->
<script type="x-template" id="vfoot">
<!-- 只能有一个根元素 -->
<!-- 解决方法二:只显示一个 -->
<div class="foot1" v-if="true">{{ msg }}</div>
<div class="foot2" v-else>{{ msg }}</div>
</script>
<script>
// 定义组件
let VHead = {
template:"#vhead",
// 组件的数据
// 简写
// 组件并不渲染真实dom,是交给根实例去渲染,所以需要在组件中的data是一个函数且需要return
data(){
return {
url:"https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png",
List:[1,2]
}
}
}
let VFoot ={
template:"#vfoot",
data(){
return {
msg:"only one foot"
}
}
}
let app = new Vue({
el:"#app",
// 局部注册组件
components: {
"v-head":VHead,
"v-foot":VFoot
}
})
</script>
看完这个例子再回去看组件的注意事项,书写风格,单文件组成,会有具体的理解
通过官方脚手架,手动搭建模块化,工程化,自动化开发环境
官方文档https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
安装vue,项目配置及启动项目
// 安装
// 1.查看是否已经安装
vue -V //查看版本
// 提示不是命令则未安装
//非3.x/4.x/5.x 时需要先卸载
npm unstall vue-cli -g
// 2.安装vue
npm install -g @vue/cli //装3/4/5
npm install -g @vue/cli-init //桥接2
//3.测试
vue -V
// 此时vue -V说还不是命令则需要配置vue的环境变量
// 创建项目的配置
// 1.在要创建项目的目录下初始开发环境
vue create 项目名
// 2. 选择自定义
// 3. 选中 babel-vue2
// 4. 选择各个包的配置文件不都放置在package.json,即每个包的配置文件都独立显示
// 5.是否保存此次设置,可以选择y/n,y的话起个名字下次创建项目选择这个名字就能生成项目
// 启动项目
// 运行服务
npm run serve
// 若能正常启动,且通过默认端口访问到则说明项目启动成功
// 浏览器不能识别vue文件,只能识别html,css,js等文件,所以在运行服务的时候这个服务会将vue等文件打包之后实时发送到浏览器,这个包是存在运行内存中的,并不会直接生成打包之后的文件,这些打包文件随着服务的关闭而销毁
// 打包项目
// 正式上线
npm run build
//问题汇总。
1. vue : 无法加载文件 ...,因为在此系统上禁止运行脚本
解决: https://blog.csdn.net/wqnmlgbsz/article/details/100654258
hbx内部设置当前策略: Set-ExecutionPolicy -Scope CurrentUser -> RemoteSigned
2. vue-cli安装失败
分析:安装全局包 登录账号要是admin | 管理运行 cmd | powersheel ,node要全量安装
解决:
a 换手机5g ,关防火墙,重装
b node要全量安装
c hbx自动创建
d npx vue create 目录
e copy 被人 创建好的项目
HbuildX自动搭建模块化,工程化,自动化开发环境
工作区-》右键-》创建项目-》普通项目-》vue-cli项目
//测试
工作区-》右键-》外部命令->npm run serve
通过脚手架生成的目录/文件
默认生成的是单页面(public->index.html)
main.js的解释
Vue.config.productionTip = false//设置为 false 以阻止 vue 在启动时生成生产提示。
new Vue({
// render: h => h(App)的推导过程
// 渲染render
/* render: function(createElement){
//业务 创建vdom
// createElement(vdom|组件)
// return createElement(App)
return createElement('div','box')
}, */
/* render: (createElement)=>{
//业务 创建vdom
// createElement(vdom|组件)
// return createElement(App)
return createElement('div','box')
}, */
// render: createElement=>createElement('div','box'),
// render: h=>h('div','box'),
render: h=>h(App),
}).$mount('#app')
}
浏览器浏览文件的是public下的index.html,这个脚手架会把所有vue渲染的东西添加到
内,然后再找到根实例main.js,main.js会打开App.vue,再找到子组件
es6模块化
输入
import a from './mod/a.js' //输入 默认输出 的任意类型
import {对外变量,a,b,c} from './mod/a.js' //输入批量输出的 属性,需要一个对象包装
import * as a from './mod/a.js' //输入批量输出的 属性,包装到一个对象里面使用
import规则
输出
//批量输出 any 导出多次
export const 对外变量 = 模块内部的变量
export {a,b,c} //批量输出模块内部的属性
//默认输出 any 只能导出一次
export default any
css 规则
style-loader 插入到style标签,style标签多了选择器冲突问题就出来了,解决方案如下
/* 方案1: 命名空间 推荐BEM命名风格*/
/*B__E--M block 区域块 element 元素 midiler 描述|状态 */
.search{}
.search__input{}
.search__input--focus{}
.search__input--change{}
.search__button{}
// B__E--M
// b-b-b__e-e-e--m-m-m
<template>
<div :class="$style.box">
...
div>
template>
<script>
export default {
mounted(){
this.$style //返回一个对象 {选择器:'混淆后的选择器',xx:oo}
}
}
script>
<style module>
/* 注意: 选择器名(支持 id,类,或者id、类开头其他选择器) */
.box{}
#box2{}
.box3 div{}
#box3 div{}
style>
<template>template>
<script>script>
<style scoped>style>
根据上面分析访问程序的顺序
在App.vue中的template中使用子组件,在export default中注册引入的子组件;子组件中的template定义子组件,在export default中写数据
官方文档
父组件通过属性绑定,子组件通过选项props接收,props是响应式的,props完成单向下行绑定
父传
<子 :自定义属性="父数据">..>
子收
props:['自定义属性']
props:{自定义属性:{type/default/required/validator...}}
<div>
{{自定义属性}}
div>
注意:
props是只读的,不推荐改
props命名:
props: [‘postTitle’]
特殊情况
数据的深浅拷贝
在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态
通过自定义事件实现,给子组件绑定自定义事件,子组件触发自定义事件时传递,事件函数是父组件方法,父方法负责接收
父绑定父接收
<template>
..
<子 @自定义事件="父方法">..>
..
template>
<script>
export default {
methods:{
父方法(接受数据){处理}
}
}
script>
子触发子传递
<script>
this.$emit('自定义事件',子.数据名)
script>
app.vue
// template模板只包裹一段元素,本身不会被渲染
<template>
<div id="app">
<!-- 自定义属性="数据" -->
<v-child1 :Tochile1="Tochile1Name" :data2To1="getChild2Date"></v-child1>
<!-- @toChild2是子组件的自定义事件 -->
<!-- getChild2是父方法 -->
<v-chiled2 @toChild2="getChild2"></v-chiled2>
<div id="mainApp">
{{ "这是从chiled2接收的数据 " + getChild2Date }}
</div>
</div>
</template>
<script>
import chiled1 from "./components/chiled1.vue";
import chiled2 from "./components/chiled2.vue";
export default {
components: {
"v-child1": chiled1,
"v-chiled2": chiled2,
},
// 组件并不渲染真实dom,是交给根实例去渲染,所以需要在组件中的data是一个函数且需要return
data() {
return {
// 传递给chiled1的数据
Tochile1Name: "Tochile1",
// 父要准备响应式的数据位,才能响应式显示从子接收到的数据
// 接收chiled2传过来的数据
getChild2Date: "-",
};
},
methods: {
// 接收第二个子传过来的数据
getChild2(data2) {
// 将chuled2传过来的数据赋给原来准备好的数据位
this.getChild2Date = data2;
},
},
};
</script>
chiled1.vue
<template>
<div id="child1">
{{ msg1 }}
<div id="parent">
{{ Tochile1 }}
<button @click="change">
修改父组件传过来的值{{
Tochile1
}}(不建议修改,虽然能成功但是控制台会报错)
</button>
<br />
{{ "这是从chiled2传给app的,app再传给chiled1" + data2To1 }}
</div>
</div>
</template>
<script>
export default {
data() {
return { msg1: "child1" };
},
// 应该只读
props: {
Tochile1: {
// vue封装了原生的所有的类型
type: String,
},
data2To1: {
type: String,
},
},
// 不建议修改
methods: {
change() {
this.Tochile1 = "changeAfterTochile1";
},
},
};
</script>
chiled2.vue
<template>
<div id="chiled2" >
<h2>{{ msg2 }}</h2>
</div>
</template>
<script>
export default ({
data(){
return{
msg2:"This is chiled2",
data2:"chiled2 to Parent'data"
}
},
// 钩子函数
// 渲染完成时将数据传给父
mounted () {
// 父组件的自定义事件,要传递的数据
this.$emit('toChild2', this.data2);
}
})
</script>
把数据存到根实例的data选项,其他组件直接修改或者使用
定义
new Vue({
data:{a:1}
})
使用
//组件内部
this // 组件本身
this.$root // vm
this.xx //组件内部数据
this.$root.a //根实例数据
this.$root.proname=value //修改
vm.xx //其他模块需要输入实例化对象vm
main.js 根实例定义
new Vue({
render: h => h(App),
data:{
data1:"根实例数据1",
data2:"根实例数据2",
data3:"根实例数据3"
}
}).$mount('#app')
模块内调用
data() {
return {
appData:this.$root.data1
};
},
{{ "chiled1使用root数据,this可以省略,因为这个组件在根实例下,this代表组件本身;" + $root.data2 }}
组件调用时,给其内部插入内容,如:<组件>内容组件>
,又叫:插槽,投射,插入到组件内容的可以是dom或组件
使用组件
<组件>123组件>
<组件><div>123div>组件>
<组件><组件>组件>组件>
<组件名><元素 slot="槽名">...
<组件名><template #槽名>..
<组件名><template slot="槽名">...
<组件名><template v-slot:槽名>...
组件内部:
<slot>匿名slot>
<slot name=槽名>slot>
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译
使用组件
<template>
<div class="app">
<v-header> v-header>
<hr />
<v-header>
<a href="" slot="more">更多1a>
<h3 slot="title">OA1h3>
v-header>
<hr />
<v-header>
<template slot="title">
<h3>OA2h3>
<h3>OA2h3>
template>
<template #more>
<a href="#">更多2a>
template>
v-header>
<hr />
<v-header>
<template v-slot:title>
<h3>OA3h3>
<h3>OA3h3>
<h3>OA3h3>
template>
<template v-slot:more>
<a href="#">v-slot:morea>
template>
v-header>
div>
template>
<script>
import Header from "./components/chiled1.vue";
export default {
components: {
//局部注册
"v-header": Header,
},
};
script>
组件内部
<template>
<div class="header">
<slot name="title">slot>
<nav>
<a href="#">xxxa>
<a href="#">xxxa>
<a href="#">xxxa>
<a href="#">xxxa>
nav>
<slot name="more">slot>
div>
template>
尽管存在 prop 和事件,有的时候仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,可以通过 ref 这个 attribute 为子组件赋予一个 ID 引用
引用元素(dom,组件<类,函数>)
官方链接1(api)
官方链接2(用法)
//父 template
<子 ref="自定义子名称"></..>
<div ref="自定义子名称">
</div>
//父 script
this.$refs.自定义子名称.数据名
this.$refs.自定义子名称.方法()
$refs 只会在组件渲染完成之后生效,并且它们不是响应式的,避免在模板或计算属性中访问 $refs
<template>
<div class="app">
<div ref="box1">box1div>
<div ref="box2">box2div>
div>
template>
<script>
export default {
// $refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。
mounted() {
console.log("mounted", this.$refs);
// 在普通的 DOM 元素上使用,引用指向的就是 DOM 元素
this.$refs.box1.style.background = "red"; //dom 图形 媒体
},
};
script>
使用一些别人开发好的单组件、组件库、插件、ui库,来提高项目开发进度,组件库通常是某公司开发并开源出来,获取方式可以通过npm、github查询,或者百度vue组件库,这里有一些推荐1、推荐2
第三方单组件的使用方式
//安装
npm i vue-swipe -D 安装
//引入
import './node_modules/vue-swipe/dist/vue-swipe.css'; 引入样式
import { Swipe, SwipeItem } from 'vue-swipe'; 引入组件
Vue.component('swipe', Swipe); //注册安装到全局
Vue.component('swipe-item', SwipeItem);
//注册到选项components 私有使用 自写样式避免与vue-swiper.css冲突 scoped
例子:vue轮播图插件vue-swipe的使用
文档:https://www.npmjs.com/package/vue-swipe
根据文档说明使用
<template>
<div class="app">
<swipe class="my-swipe" :auto="1000" :showIndicators="false">
<swipe-item class="slide1">1swipe-item>
<swipe-item class="slide2">2swipe-item>
<swipe-item class="slide3">3swipe-item>
swipe>
div>
template>
<script>
// 导入
require("vue-swipe/dist/vue-swipe.css");
import { Swipe, SwipeItem } from "vue-swipe";
export default {
components: {
// 注册到局部
// 组件名:组件变量名
swipe: Swipe,
// "swipeItem": SwipeItem,
// 编译的时候会组件名的大驼峰转换成xx-xx,上面的这种写法完全等同于下面这种写法
// swipeItem === swipe-item // true
"swipe-item": SwipeItem,
},
};
script>
<style>
.my-swipe {
height: 200px;
color: #fff;
font-size: 30px;
text-align: center;
}
.slide1 {
background-color: #0089dc;
color: #fff;
}
.slide2 {
background-color: #ffd705;
color: #000;
}
.slide3 {
background-color: #ff2d4b;
color: #fff;
}
style>
pc端、后台管理
移动端、客户端
通用
官方文档
用来SPA (single page application 单页面应用),页面跳转
页面模式 | 多页面模式(MPA Multi-page Application) | 单页面模式(SPA Single-page Application) |
---|---|---|
页面组成 | 多个完整页面, 例如page1.html、page2.html等 | 由一个初始页面和多个页面模块组成, 例如:index.html |
公共文件加载 | 跳转页面前后,js/css/img等公用文件重新加载 | js/css/img等公用文件只在加载初始页面时加载,更换页面内容前后无需重新加载 |
页面跳转/内容更新 | 页面通过window.location.href = "./page2.html"跳转 | 通过使用js方法,append/remove或者show/hide等方式来进行页面内容的更换 |
数据的传递 | 可以使用路径携带数据传递的方式,例如:http://index.html?account=“123”&password=123456"",或者localstorage、cookie等存储方式 | 直接通过参数传递,或者全局变量的方式进行,因为都是在一个页面的脚本环境下 |
用户体验 | 如果页面加载的文件相对较大(多),页面切换加载会很慢 | 页面片段间切换较快,用户体验好,因为初次已经加载好相关文件。但是初次加载页面时需要调整优化,因为加载文件较多 |
场景 | 适用于高度追求高度支持搜索引擎的应用 | 高要求的体验度,追求界面流畅的应用 |
转场动画 | 不容易实现 | 容易实现 |
单页面模式:相对比较有优势,无论在用户体验还是页面切换的数据传递、页面切换动画,都可以有比较大的操作空间 多页面模式:比较适用于页面跳转较少,数据传递较少的项目中开发,否则使用cookie,localstorage进行数据传递,是一件很可怕而又不稳定的无奈选择
安装引入注册
npm i vue-router -S
// src/main.js
import router from './plugins/router.js'
new Vue({
router
})
配置路由
// src/plugins/router.js
import Vue from 'vue'
//1. 引入路由包
import VueRouter from 'vue-router'
//2. 安装插件包到Vue上,
Vue.use(VueRouter);
//3. 路由配置
let routes = [
{path: '/home',component: Home}, //route 一条路由的配置
]
//4.路由实例
let router = new VueRouter({ //插件路由对象
// routes:routes
routes,
});
//5.导出路由实例,让他去控制vue根
export default router
一些注意事项
vue.use安装插件功能,包含注册功能
路由:"/"是所有的/xx的父
声明式跳转都是调用了$router,添加历史记录(原生history)
如何把下一级的展示区显示在上一级级里面?
1.path:在上一级里面不再定义children,再定义一个下一级,如:path:‘二级/三级’
2.下一级里的链接对应这个定义的,同时下一级级不再定义展示区,不定义展示区的话就会寻找上一级展示区default,达到我们想要的效果
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
// 路由控制根
router,
render: h => h(App)
}).$mount('#app')
router/index.js
// 默认导入的是node_modules下的包
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入自己写的文件
import Home from '../views/Home.vue'
// vue.use安装插件功能,包含注册功能
Vue.use(VueRouter)
// 配置路由
// 同级路由自上向下匹配
// 所以把重定向和404放到最后
// 路由:"/"是所有的/xx的父
const routes = [{
// 路径
path: '/home',
// 路由名,可通过属性绑定形式:to="{name:'Home'}"实现跳转
name: 'Home',
// 组件
component: Home
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
},
{
path: '/more',
// 当某个路由有子级路由的时候,这时候父级路由需要一个默认的路由,所以父级路由不能定义name属性
// name: 'More',
component: () => import('../views/More.vue'),
// 子路由
// 子路径直接写,前面会加上父路径
// 类型类似json,[{}]
children: [{
path: 'more1',
name: 'More1',
component: () => import('../views/more/more1.vue')
},
{
path: 'more2',
name: 'More2',
component: () => import('../views/more/more2.vue')
},
// more2的三级标题
// 在二级里面不再定义children
// 直接定义下一级
{
path: 'more2/2',
name: 'More2-2',
component: () => import('../views/more/default.vue')
},
{
path: 'more3',
name: 'More3',
component: () => import('../views/more/more3.vue')
},
// 子路由的默认页
{
path: '',
redirect: 'more1'
}
]
},
{
// 重定向
// 默认页
path: '/',
// 配置型跳转
redirect: '/home'
},
{
// 404页面
// paht全部匹配,放在最后,以上path都找不到就跳转404
path: '*',
name: 'NotFound',
component: () => import('../views/NotFound.vue')
}
]
// 实例化路由
const router = new VueRouter({
// routes:routes
routes,
// mode:'hash'// 默认哈希模式 原理:location.href
mode: 'history' // 历史记录 原理:history.pushState
})
// 导出路由实例,控制vue的根
export default router
App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/home">Homerouter-link> |
<router-link to="/about">Aboutrouter-link> |
<router-link to="/more">Morerouter-link>
div>
<router-view/>
div>
template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
}
#nav a {
font-weight: bold;
color: #2c3e50;
}
/* 严格匹配 */
/* 当前元素选中的样式 */
#nav a.router-link-exact-active {
color: #42b983;
}
/* 模糊匹配 */
/* 父元素选中 */
.router-link-active{
background: #68829c;
}
style>
more2.vue
<template>
<div id="more2">
<h1>This is more2!!h1>
<div id="nav">
<router-link to="/more/more2/2">more2-1router-link>|
<router-link to="/more/more2/2">more2-2router-link>|
<button @click="go">more2-3编程式跳转button>
div>
div>
template>
<script>
export default {
methods: {
go() {
// console.log(this.$router);
// 字符串
// this.$router.push('/more/more2/2')
// 对象
// this.$router.push({ path: "/more/more2/2" });
// 命名的路由,可在vue插件中查看params中查看参数
// this.$router.push({ name: "More2-2", params: { userId: "123" } });
// 带查询参数,变成 /2?plan=private
this.$router.push({ path: "/more/more2/2", query: { plan: "private" } });
},
},
};
script>
官方文档
全局守卫
// src/plugins/router.js
//前置
router.beforeEach((to, from, next) => {
// to: 目标路由 $route
// from: 当前路由 $route
// next() 跳转 一定要调用
next(false);//走不了
next(true);//走你
next('/login')//走哪
next({path:'/detail/2',params:{},query:{}})//带些参数
// 守卫业务
if(to.path=='/login' || to.path=='/reg' || to.path=='/register'){
//判断是不是登录了
//axios请求 携带 token
next()
}else{
next('/login');
}
})
//后置
router.afterEach((to,from)=>{
//全局后置守卫业务
// 如清理数据,关闭服务等
})
路由独享守卫
// src/plugins/router.js -> routes
{
path: '/user',
component: User,
beforeEnter: (to,from,next)=>{ //路由独享守卫 前置
console.log('路由独享守卫');
if(Math.random()<.5){
next()
}else{
next('/login')
}
}
},
注意路由独享,没有后置
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。
可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
组件内部守卫
//组件内部钩子
beforeRouteEnter (to, from, next) {//前置 运行在beforeCreate 之前
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {//后置 运行在beforeDestory之前
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
定义路由的时候配置 meta
字段
//src/plugins/router.js
{
path: '/home',
component: Home,
meta: { requiresAuth: true }
}
访问 meta
字段
this.$route.meta
to.meta from.meta
SPA是单页面,只有一个滚动条,路由跳转时滚动条会影响到元素位置,使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样
对于所有路由导航,简单地让页面滚动到顶部
// src/plugins/router.js
const router = new VueRouter({
scrollBehavior (to, from, savedPosition) {
//计算位置
return { x: 0, y: 0 }
}
})
因为内容太多,csdn的富文本编辑器渲染性能下降,无奈只能写续篇
文章地址
https://blog.csdn.net/weixin_44902117/article/details/121116657