1.slot插槽(内容分发)
内容分发:把父组件的内容分发到子组件里面 a.单个slot b.具名slot(具有名字的插槽) 混合父组件的内容与子组件自己的模板-->内容分发 父组件模板的内容在父组件作用域内编译; 子组件模板的内容在子组件作用域内编译
<body>
<div id="box">
<child>
<div slot='a'>aaaaaa</div>
<div slot="b">bbbbbb</div>
</child>
<swiper>
<li v-for='data in dataList'>
{{data}}
</li>
</swiper>
</div>
<script>
Vue.component('child',{
template:`<div>
<slot name='a'></slot>
child
<slot name='b'></slot>
</div>`
})
Vue.component("swiper",{
template:`<div>
<ul>
<slot></slot>
</ul>
</div>`
})
new Vue({
el:"#box",
data:{
dataList:['111','222','333']
}
})
</script>
</body>
实例
<body>
<div id="box">
<!-- <button @click='isShow=!isShow'>click</button> -->
<navbar>
<button @click='isShow=!isShow'>navbar-button</button>
</navbar>
<sidebar v-show='isShow'></sidebar>
</div>
<script>
Vue.component('navbar',{
template:`<div>
navbar
<slot></slot>
</div>`,
}),
Vue.component('sidebar',{
template:`<div style='background:yellow;width:200px;'>
<ul>
<li>1111</li>
<li>1111</li>
<li>1111</li>
</ul>
</div>`
})
new Vue({
el:"#box",
data:{
isShow:false
},
})
</script>
</body>
2.transition过渡
Vue在插入、更新或者移除DOM时,提供多种不同方式的应用过渡效果。
1.单元素/组件过渡
CSS过渡
CSS动画
结合animate.css动画库
<script src="../js/vue.js"></script>
<style>
.kerwinfade-enter-active,.kerwinfade-leave-active{
transition:all 1.5s;
}
.kerwinfade-enter,.kerwinfade-leave-to{
opacity:0;
transform:translateX(100px);
}
.kerwinbounce-enter-active{
animation:bounce-in .5s;
}
.kerwinbounce-leave-active{
animation:bounce-in .5s reverse;
}
@keyframes bounce-in{
0%{
opacity: 0;
transform:translateX(100px);
}
100%{
opacity: 1;
transform: translateX(0px);
}
}
</style>
<body>
<div id="box">
<button @click='isShow=!isShow'>click</button>
<transition name='kerwinfade'>
<div v-show='isShow'>11111</div>
</transition>
<transition name='kerwinbounce'>
<div v-show='isShow'>22222</div>
</transition>
</div>
<script>
var vm = new Vue({
el:"#box",
data:{
isShow:false
}
})
</script>
</body>
2.多个元素过渡(设置key)
多个元素过渡解决方案:
1. 标签名不同(diff算法标签名相同会复用),采用v-if v-else
2. 当有相同标签名的元素切换时,需要通过key特性设置唯一的值来标记以让Vue区分它们,否则Vue为了效率只会替换相同标签内部的内容。
mode:in-out 先来后走
mode:out-in 先走后来
不设置的话,多个元素同时进行
<style>
.kerwinbounce-enter-active{
animation:bounce-in .5s;
}
.kerwinbounce-leave-active{
animation:bounce-in .5s reverse;
}
@keyframes bounce-in{
0%{
opacity: 0;
transform:translateX(100px);
}
100%{
opacity: 1;
transform: translateX(0px);
}
}
</style>
<body>
<div id="box">
<button @click='isShow=!isShow'>click</button>
<transition name='kerwinbounce' mode='in-out'>
<p v-if='isShow' key='1'>11111</p>
<!-- <div v-else>22222</div> -->
<p v-else key='2'>22222</p>
</transition>
</div>
<script>
var vm = new Vue({
el:'#box',
data:{
isShow:false
}
})
</script>
</body>
3.多个组件过渡
<script src="../js/vue.js"></script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: all 1.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
transform: translateX(100px);
}
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
opacity: 0;
transform: translateX(100px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
</style>
<body>
<div id="box">
<keep-alive>
<transition name='bounce' mode='out-in'>
<component :is='who'></component>
</transition>
</keep-alive>
<footer>
<ul>
<li><a @click='who="home"'>首页</a></li>
<li><a @click='who="list"'>列表页</a></li>
<li><a @click='who="shopcar"'>购物车页面</a></li>
</ul>
</footer>
</div>
<script>
var vm = new Vue({
el:"#box",
data:{
who:'home'
},
components:{
'home':{
template:`<div>home</div>`
},
'list':{
template:`<div>list</div>`
},
'shopcar':{
template:`<div>shopcar</div>`
}
}
})
</script>
</body>
4.列表过渡(设置key)
<transition-group>不同于transition,
它会以一个真实元素呈现:默认为一个<span>。
你也可以通过tag特性更换为其他元素。
<script src="../js/vue.js">
/* 列表过渡(设置key)
<transition-group>不同于transition,它会以一个真实元素呈现:默认为一个<span>
你也可以通过tag特性更换为其他元素
提供唯一的Key属性值
*/
</script>
<style>
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
@keyframes bounce-in {
0% {
opacity: 0;
transform: translateX(100px);
}
100% {
opacity: 1;
transform: translateX(0px);
}
}
</style>
<body>
<div id="box">
<input type="text" v-model='mytext'>{{mytext}}
<button @click='handleAddClick'>add</button>
<!-- <ul> -->
<transition-group tag='ul' name='bounce'>
<li v-for='(data,index) in dataList' :key='data'>
{{data}}----{{index}}
<button @click='handleDelClick(index)'>del</button>
</li>
</transition-group>
<!-- </ul> -->
</div>
<script>
var vm = new Vue({
el:"#box",
data:{
mytext:'',
dataList:[]
},
methods:{
handleAddClick(){
},
handleDelClick(index){
}
}
})
</script>
</body>
3.生命周期
- 生命周期各个阶段
- 生命周期钩子函数的触发条件与作用
<body>
<div id="box">
<input type="button" value="修改msg" @click="msg='No'" />
<h3 id="h3">{{msg}}</h3>
</div>
<script>
var vm = new Vue({
el: "#box",
data: {
msg: "ok",
},
methods: {
show() {
console.log("执行了show方法");
},
},
beforeCreate() {
//这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
//console.log(this.msg);
//this.show();
/* 注意:在beforeCreate生命周期函数执行的时候,data和methods中的数据都还没有初始化 */
},
created() {
//这是遇到的第二个生命周期函数
//console.log(this.msg); //ok
//this.show(); //执行了show方法
/* 在created中,data和methods都已经被初始化好了! */
/* 如果要调用methods中的方法,或者操作data中的数据,最早只能在created中操作 */
},
beforeMount() {
//这是遇到的第三个生命周期函数,表示模板已经在内存中编辑完成了,但是尚未把模板渲染到页面上
//console.log(document.getElementById("h3").innerText); //{{msg}}
/* 在beforeMount执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串 */
},
mounted() {
//这是遇到的第四个生命周期函数,表示内存中的模板已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
//console.log(document.getElementById("h3").innerText);
/* 注意:mounted是实例创建期间的最后一个生命周期函数,当执行完mounted就表示,实例已经被完全创建好了,此时,如果没有其他操作的话,这个实例就静静的躺在我们的内存中一动不动 */
},
//接下来的是运行中的两个事件
beforeUpdate() {
//这时候,表示我们的页面还没有被更新,数据肯定被更新了
//console.log(document.getElementById("h3").innerText);//Ok
//console.log("data中的msg数据是:" + this.msg);//No
/* 得出结论:当执行beforeUpdate的时候,页面中的显示的数据,还是旧的,此时data数据是最新的,页面尚未和最新的数据保持同步 */
},
updated() {
console.log(document.getElementById("h3").innerText);
console.log("data中的msg数据是:" + this.msg);
/* update事件执行的时候,页面和data数据已经保持同步了,都是新的 */
},
beforeDestory() {},
destroyed() {},
});
</script>
</body>
过滤器
<body>
<div id="box">
<ul>
<li v-for='data in dataList' :key='data.id'>
<h3>{{data.nm}}</h3>
<!-- <img :src="changepath(data.img)"> -->
<img :src="data.img | kerwinpath">
</li>
</ul>
</div>
<script>
Vue.filter("kerwinpath",function(data){
return data.replace('w.h','128.180')
})
var vm = new Vue({
el:"#box",
data:{
dataList:[]
},
mounted(){
axios.get('test.json').then(res=>{
console.log(res.data);
this.dataList=res.data.movieList
})
},
methods:{
// changepath(path){
// return path.replace('w.h','128.180')
// }
}
})
</script>
</body>
4.swiper学习
<body>
<div id="box">
<div class="swiper-container a">
<div class="swiper-wrapper">
<div class="swiper-slide" v-for='data in dataList'>
{{data}}
</div>
</div>
<div class="swiper-pagination"></div>
</div>
</div>
<script>
new Vue({
el:"#box",
data:{
dataList:[]
},
mounted(){
setTimeout(()=>{
this.dataList=['1111','2222','3333'] // 状态改完,异步更新dom
// swiper 初始化过早
},2000)
},
updated(){
new Swiper(".a",{
loop:true,
pagination:{
el:"'.swiper-pagination'"
}
})
}
})
</script>
</body>
swiper组件封装
自定义封装swiper组件(基于swiper)
注意:防止swiper初始化过早
<body>
<div id="box">
<swiper :key="{datalist.length;">
<div class="swiper-slide" v-for="data in datalist">
{{data}}
</div>
</swiper>
</div>
<script>
Vue.component("swiper", {
template: `<div class="swiper-container a">
<div class="swiper-wrapper">
<slot></slot>
</div>
<div class="swiper-pagination"></div>
</div>`,
mounted() {
new swiper(".a", {
//direction:'vertical'
loop: true,
//如果需要分页器
pagination: {
el: ".swiper-pagination",
},
});
},
});
new Vue({
el: "#box",
data: {
datalist: [],
},
mounted() {
setTimeout(() => {
this.datalist = ["111", "222", "333"];
}, 2000);
},
});
</script>
</body>
6.自定义指令
自定义指令:操作底层dom
定义在谁身上,就能拿到谁的dom节点
<body>
<div id="box">
<div v-hello='"red"'>111111</div>
<div v-hello='"yellow"'>222222</div>
<div v-hello='mycolor'>333333</div>
</div>
<script>
Vue.directive('hello',{
inserted(el,bind){
// 指令-生命周期-创建 只会执行一次
// 插入
// console.log(el)
// console.log(bind.value)
el.style.background=bind.value
},
update(el,bind){
// 指令-生命周期-更新
el.style.background=bind.value
}
})
var vm = new Vue({
el:'#box',
data:{
mycolor:'blue'
}
})
</script>
</body>
指令轮播
<body>
<div id="box">
<div class="swiper-container a">
<div class="swiper-wrapper">
<div
class="swiper-slide"
v-for="(data,index) in list"
v-swipe="{
index:index,
length:list.length
}"
>
{{data}}
</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
</div>
</div>
<script>
Vue.directive("swipe", {
inserted(el, bind) {
console.log(bind.value);
if (bind.value.index === bind.value.length - 1) {
new Swiper(".a", {
loop: true,
pagination: {
el: ".swiper-pagination",
},
});
}
},
});
new Vue({
el: "#box",
data: {
list: [],
},
mounted() {
setTimeout(() => {
this.list = ["111", "222", "333"];
}, 2000);
},
});
</script>
</body>
7.单文件/多文件组件
创建.vue文件,要想让单文件被使用
需要用到webpack将vue文件转为html文件、es6转es5,脚手架来配置webpack
只安装一次脚手架就行,一个全局就够了
npm install -g @vue/cli(一次安装) node-sass需要单独处理
#or
yarn global add @vue/cli
vue-cli3.0的使用
npm install -g @vue/cli(一次安装) node-sass需要单独处理
按住shift,右键,可以直接进入该目录下的windows powerShell中
创建一个项目
vue create myapp
npm run serve 开发环境构建
npm run build 生产环境构建
npm run lint 代码检测工具(会自动修正)
yes
manually select features
Babel(babel es6转es5) Router,Vuex(复杂状态管理), CSS pre-processors(CSS预处理器,想用SCSS、less可以安装), Linter/Formatter(eslint规范,保持风格一致性,该用双引号还是单引号等)
yes
Sass/SCSS(with node-sass)
Standard
Lint an fix on commit
In dedicated config files
yes
自定义名字
如果遇到斜杠等无法安装,直接ctrl+c退出 手动去目录下删掉module文件,
然后cd myapp进入myapp中,执行cnpm i接着安装package.json里面的模块
入口文件main.js
启动 cnpm run serve
查看ip 进入浏览器输入localhost:8080 启动成功
下载 Vetur插件
npm run lint 自动修正代码
npm run build 上线代码,生成dist文件夹交给后端
入口页面 public/index.html
<div id="box"></div>
入口文件 src/main.js
根组件 src/App.vue
main.js
import Vue from 'vue' // ES6 模块导入方式
import App from './App.vue'
// import router from './router'
// import store from './store'
Vue.config.productionTip = false
new Vue({
// router,
// store,
render: h => h(App)
}).$mount('#box')
<template> -html代码,最多可以包含一个
<script> -js代码,最多可以包含一个
<style> -css代码,可以包含多个,src的路径是相对的
style标签 加上scoped属性,css局部生效
style标签 加上lang='scss' 支持scss
1.vue create *** 一个项目
2.写项目(在src下编译 ,vue组件放在src中的component下)
npm run serve 开发环境构建
npm run build 生产环境构建
npm run lint 代码检测工具(会自动修正)
3.vs终端上面,cd myapp中,输入ls,输入npm run lint自动修复错误
4.上线项目
新开一个终端,右键主文件,在终端打开 即myapp下
输入 npm run build,自动生成dist文件
express版本中将命令工具分家出来了(项目地址:https://github.com/expressjs/generator),所以我们还需要安装一个命令工具,命令如下:
npm install -g express-generator express-generator 是项目生成器
express --version
右键进入项目所在的路径中的windows powershell中
1.输入express -e backend
2.cd backend
3.cnpm i
4.复制dist里面的文件到backend里的public文件夹中
5.后端启动项目 npm start
6.浏览器打开localhost:3000
反向代理配置
跨域:是指浏览器不能执行其他网站的脚本,它是由浏览器的同源策略造成的,是浏览器对JS实施的安全限制
无法跨域是浏览器对于用户安全的考虑,如果自己写个没有同源策略的浏览器,完全不用考虑跨域问题
同源策略限制了以下行为
Cookie、 LocalStorage和IndexDB无法读取
DOM和JS对象无法获取
Ajax请求发送不出去
狭义的同源就是指:域名、协议、端口均为相同
后端接口与前端接口一定会有跨域的现象
前端localhost:8080
后端localhost:3000
例:8080请求猫眼 跨域
在network中随便点击一个文件,发现Access-Control-Allow-Origin: https://maoyan.com 这是允许请求
但是有一些网站不允许请求,需要配置反向代理
cd myapp文件夹下输入npm install --save axios(下载库)
引入库 import axios from 'axios'
调用接口
反向代理配置 vue.config.js是一个可选的配置文件,如果项目的(和 package.json同级的)根路径存在这个文件,那么它会被@vue/cli-service自动加载(https://cli.vuejs.org/zh/config/#css-loaderoptions)
创建一个vue.config.js文件
如果你的前端应用和后端 API 服务器没有运行在同一个主机上,你需要在开发环境下将 API 请求代理到 API 服务器。
这个问题可以通过 vue.config.js 中的 devServer.proxy 选项来配置。
只要更改了配置文件,一定要重启服务器 ctrl+ c npm start
App.vue
<template>
<div>
hello vue
<input type = "text" ref='mytext'>
<button @click='handleAdd()'>add</button>
<ul>
<li v-for = 'data in dataList' :key='data'>
{{ data }}
</li>
</ul>
<navbar>
<button @click="isShow=!isShow">navbar-click</button>
</navbar>
<sidebar v-show='isShow'></sidebar>
</div>
</template>
<script>
// commonJS module.exports
import navbar from './components/Navbar'
import sidebar from './components/Sidebar'
import axios from 'axios'
// 全局组件
// import Vue from 'vue'
// Vue.component('navbar', navbar)
// Vue.component('sidebar', sidebar)
// ES6 导出
export default {
data () {
return {
dataList: [],
isShow: false
}
},
methods: {
handleAdd () {
// console.log('add', this.$refs.mytext.value)
this.dataList.push(this.$refs.mytext.value)
}
},
mounted () {
axios.get('/ajax/mostExpected?ci=264&limit=10&offset=0&token=&optimus_uuid=05344D607A4411EAA227BF8336DA36C76B6E77239DE44EFD988866B692B8D66B&optimus_risk_level=71&optimus_code=10').then((res) => {
console.log(res.data)
})
},
components: {
navbar: navbar,
sidebar: sidebar
}
}
</script>
<style lang="scss">
ul {
list-style: none;
li {
background:yellow
}
}
</style>
navbar.vue
<template>
<nav>
navbar-<slot></slot>
</nav>
</template>
sidebar.vue
<template>
<aside>
sidebar
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</aside>
</template>
<style lang="scss" scoped>
ul {
li {
background:blue
}
}
</style>
vue.config.js
module.exports = {
devServer: {
proxy: {
'/ajax': {
target: 'https://m.maoyan.com',
// ws: true,
changeOrigin: true
},
/* '/foo': {
target: '<other_url>'
} */
}
}
}