22-12-19 西安 vue-cli vue-cli脚手架、组件化编程、插槽、vue-router路由、Vuex共享数据

"对A,我还剩一张牌啦!"

"呃。。。要不起

这俩天被洗脑了,看了好几个主播的“鸭鹅杀”。脑子里总是那一句:“叼得一发言:叼的一认为.....,对是不对”


Vue CLI

npm config set registry https://registry.npm.taobao.org --global

CLI (@vue/cli) 是一个全局安装的 npm 包,提供了终端里的 vue 命令。它可以通过 vue create 快速搭建一个新项目,或者直接通过 vue serve 构建新想法的原型。你也可以通过 vue ui 通过一套图形化界面管理你的所有项目。

1.安装vue cli 

在这之前,确保安装了Node(推荐 v10 以上)

npm install -g @vue/cli

下载太慢,可以先设置镜像

npm config set registry https://registry.npm.taobao.org --global

验证它是否安装成功

vue --version

如下:则安装成功

怎样查看vue-cli的安装路径

where vue

如果没有配置npm的下载路径,那么vue就会被下载到系统用户目录下

卸载vue-cli

npm uninstall vue-cli -g

结果呢,还是

1. 删除缓存

到系统盘的用户目录下有个Appdata,然后有个Roaming(缓存)如图:

2.删除环境变量

进入控制面板(我的是win10系统),点击此电脑——》系统属性——》在左边的输入框输入“高级系统”,点击“查看高级系统设置”——》选择环境变量——》用户变量和系统变量中找到“path”变量,点进去,找到“node”相关的,删掉

找到安装路径,有个卸载文件,点击就能卸载了

如果按装没有成功需要检查nodejs和NPM的版本,通过 node -v 和npm -v检查


2.创建项目

vue create 项目名

vue create vue-xiaoyumao

选择vue2 ,回车,完成后就会在桌面生成一个文件夹


3.目录结构分析

使用VsCode打开

main.js 是 npm run serve 的开端

main.js是入口文件,一般不需要改

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

每个 Vue 应用都需要通过实例化 Vue 来实现。

index.html

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--开启移动端的理想视口-->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <!--配置页签图标-->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!--配置网页标题-->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!--当浏览器不支持js时noscript中的元素就会被渲染 -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <!--容器-->
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

package.json

package.json 是前端每个项目都有的 json 文件,位于项目的根目录。package.json 里面有许许多多的配置,介绍一些常见配置,我把它们分为了 7 大类:

  • 描述配置

主要是项目的基本信息,包括名称,版本

  • 文件配置

包括项目所包含的文件,以及入口等信息。

  • 脚本配置

指定项目的一些内置脚本命令,这些命令可以通过 npm run 来执行。通常包含项目开发,构建 等 CI 命令,

  • 依赖配置
  • 发布配置
  • 系统配置
  • 第三方配置

4.npm run serve

进到项目目录,cmd打开终端,执行命令npm run serve 启动项目

补充:npm run build 命令会生成最纯粹的js,css,html

浏览器访问效果:localhost:8080


5.自定义端口

默认的项目启动后访问localhost:8080

在vue.config.js文件里配置就行

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false, // 关闭语法检查 防止不必要的语法报错
  // 配置devServer
  devServer: {
     host: 'localhost', // 项目运行的ip
     port: 8000, // 项目运行的端口号
   }
})

重启项目

但是会引起跨域的问题。

加链接头,开启跨域,加/api作为识别。意为请求/api下的链接,直接回转到target

pathRewrite:重写路径 后端识别时候把/api替换成空

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false, // 关闭语法检查 防止不必要的语法报错
  devServer: {
    host: 'localhost', // 项目运行的ip
    port: 8000, // 项目运行的端口号
    proxy: {
        '/api': {
            target: 'http://localhost:8080',
            changeOrigin: true,
            pathRewrite: {
                '/api': ''
            }
        }
    }
}
})

6.自定义界面

接下来换成我们自己写的组件进行渲染

新建pages目录,下面建2个.vue文件

新增Bedroom.vue

<template>
  <div>
    <p>{{ msg }}</p>
  </div>
</template>

<script>
export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: "Bedroom",
  data() {
    return {
      msg: "欢迎来到卧室:你要睡觉吗",
    };
  },
};
</script>

新增Kitchen.vue

<template>
  <div>
    <p>{{ msg }}</p>
  </div>
</template>

<script>
export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: "Kitchen",
  data() {
    return {
      msg: '欢迎来到厨房:你要做饭吗',
    };
  },
};
</script>

修改App.vue,引入我们自定义的2个组件并注册使用。如下:

<template>
  <div id="app">
    <Bedroom />
    <Kitchen />
  </div>
</template>

<script>
import Bedroom from "./pages/Bedroom.vue";
import Kitchen from "./pages/Kitchen.vue";
export default {
  name: "App",
  components: {
    Bedroom,
    Kitchen,
  },
};
</script>

页面打开后,渲染效果如下:


组件component

1、组件概念

每个 .vue 组件都由 3 部分构成,分别是:

  • template -> 组件的模板结构
  • script -> 组件的 JavaScript 行为
  • style -> 组件的样式

其中,每个组件中必须包含 template 模板结构,而 script 行为和 style 样式是可选的组成部分。vue 规定:每个组件对应的模板结构,需要定义到 <template> 节点中。

<template> 是 vue 提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素。

vue 2.x中,template节点内的所有元素,最外层“必须有”唯一的根节点进行包裹,否则报错

<template>
  <div>
    <p>{{ msg }}</p>
  </div>
</template>

<script> 节点

vue 规定:组件内的 <script> 节点是可选的,开发者可以在 < script> 节点中封装组件的 JavaScript 业务逻辑。< script > 节点的基本结构如下

<script>
//今后,组件相关的data数据、methods方法等,都需要定义到export default所导出的对象中。
export default {}
</script>

可以通过name属性当前组件定义一个名称(建议:每个单词的首字母大写)代码如下:

<script>
export default {
// name属性指向的是当前组件的名称(建议:每个单词的首字母大写)
name: 'MyApp'
}
</script>

data属性

vue 组件渲染期间需要用到的数据,可以定义在 data 节点中:

<script>
export default {
// 组件的名称
name: 'MyApp',
// 组件的数据(data方法中return出去的对象,就是当前组件渲染期间需要用到的数据对象)
data() {
    return {
        username: '哇哈哈',
    }
},
}
</script>

其中组件中的 data 必须是函数

组件内的 <style> 节点

vue 规定:组件内的 <style> 节点是可选的,开发者可以在 <style> 节点中编写样式美化当前组件的 UI 结构。<script > 节点的基本结构如下:


2、组件导入

第一种

自然是直接import xxx from "组件的路径",然后再到components中引用,例如vue自带的helloworld.vue示例:

import Kitchen from "@/pages/Kitchen.vue";

第二种

在原本的组件目录文件夹中添加一个index.js,

然后在这个index.js中将这些组件导出,

export { default as Kitchen } from "./Kitchen";
export { default as Bedroom } from "./Bedroom";
export { default as HelloWorld } from "./HelloWorld";

在用到这些组件的时候就可以直接在导入这个index.js时解构获取想要的组件了,如下

 注意,解构时的名字必须跟你导出的的一致,要用哪些就导入哪些组件。


3、组件嵌套

在单文件组件Bedroom.vue种引入BedsideCupboard组件并和SingleBed组件使用:

<template>
  <div>
    <BedsideCupboard />
    <SingleBed />
  </div>
</template>

<script>
import BedsideCupboard from "./BedsideCupboard.vue";
import SingleBed from "./SingleBed.vue";
export default {
  components: { BedsideCupboard, SingleBed },
  name: "Bedroom",
  data() {
    return {
      msg: "欢迎来到卧室:你要睡觉吗",
    };
  },
};
</script>

打开vue开发者工具,直观效果"组件嵌套"

看得出来,VM非常省心,只管理App组件(约定)

<template>
  <div id="app">
    <div>
      <Bedroom />
    </div>
  </div>
</template>

<script>
import { Kitchen, Bedroom } from "@/pages";
export default {
  name: "App",
  components: { Kitchen, Bedroom },
};
</script>

4、组件实例对象

VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
Vue的实例对象,以后简称vm。

组件本质
1、Bedroom组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的。
2、我们只需要写<Bedroom/>或<Bedroom></Bedroom>,Vue解析时会帮我们创建Bedroom组件的实例对象,即Vue帮我们执行的:new VueComponent(options).

3.特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!!

关于this指向:
(1)组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是vc【VueComponent实例对象】


(2)new Vue(options)配置中:
data函数、methods中的函数、watch中的函数、computed中的函数它们的this均是【Vue实例对象】

vm管理着vc

vm能通过el决定为哪个容器服务,vc不行,只能跟着大哥vm混

vc中,data必须是一个函数,vm中可以是data函数也可以是data对象


组件通信

1、父传子props

让组件接收外部传过来的数据

接收数据:
第一种方式(只接收):props:['name']
第二种方式(限制数据类型):props:{name:String}
第三种方式(限制类型、限制必要性、指定默认值):
props:{
    name:{
    type:String, //类型
        required:true, //必要性
        default:'JOJO' //默认值
    }
}


2、子传父this.$emit

触发自定义事件:this.$emit('atguigu',数据)

自定义事件是给组件使用的

由于是在<Student>标签绑定了自定义事件,要触发就找student组件。事件触发后回调用methods里的demo()方法.

//父组件中使用子组件标签 <Student v-on:atguigu='demo'>
<Student @atguigu='demo'>
 
//子组件触发父组件中demo方法
demo(){
 console.log("demo被调用了")
}
 
//子组件触发时携带参数
demo(name){               
console.log('demo被调用了',name)  //atguigu事件触发后的回调
}

 那么来看看在子组件:student组件是如何触发父组件里自定义事件atguigu

<button @click="sendStudentName" >把学生名给app</button>
 
sendStudentName(){
this.$emit('atguigu') //在组件触发自定义事件atguigu
//也可以带参传值 
//this.$emit('atguigu',"我是子组件传给父组件的值")
}

3、事件总线 EventBus

vue事件总线就像所有组件的事件中心,在组件中,我们可以使用 $emit$on$off 分别来分发、监听、取消监听事件。

将其定义在 main.js 文件中,创建在 vm 的实例对象身上,因为 vm 实例对象只有一个

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	// beforeCreate中模板未解析,且this是vm
	beforeCreate(){
		Vue.prototype.$bus = this	//安装全局事件总线
	}
})

我们可以在某个Vue页面使用。

 methods:{
  // 触发事件,事件名不能重复
  aaa(){
    this.$bus.$emit('updateMessage', '德华');
  }
},

另一个Vue页面使用

this.$bus.$on('updateMessage', function(value) {
  console.log(value);
})

同时也可以使用this.$bus.$off('sendMsg')来移除事件监听


4、this.$refs

应用在html标签上获取的是真实DOM元素,应用在组件标签上获取的是组件实例对象(vc)
使用方式:

打标识:<h1 ref="xxx"></h1> 或 <School ref="xxx"></School>
获取:this.$refs.xxx

SlOT插槽

1、默认插槽

先弄一个基本效果

创建Category.vue

<template>
  <div class="category">
    <h3>{{title}}分类</h3>
    <ul>
      <li v-for="(item,index) in listData" :key="index">{{item}}</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "Category",
  props:['listData','title']
};
</script>

<style>
.category {
  background-color: aqua;
  width: 200px;
  height: 300px;
}
h3{
    text-align: center;
    background-color: burlywood;
}
</style>

在App.vue中引入并使用Category.vue

<template>
  <div id="app" class="container">
    <Category title="中立阵营" :listData="zhongli" />
    <Category title="好人阵营" :listData="haoren" />
    <Category title="狼人阵营" :listData="lang" />
  </div>
</template>

<script>
import Category from "./components/Category.vue";

export default {
  name: "App",
  components: {
    Category,
  },
  data() {
    return {
      zhongli: ["呆呆鸟", "猎鹰", "秃鹫", "鸽子", "鹈鹕"],
      haoren: [
        "加拿大鹅",
        "观鸟者",
        "鹅",
        "网红",
        "星界",
        "警长",
        "正义使者",
        "模仿",
        "侦探",
        "通灵",
      ],
      lang: [
        "刺客",
        "间谍",
        "食鸟鸭",
        "专业杀手",
        "承办丧葬者",
        "身份窃贼",
        "忍者",
        "爆炸王",
      ],
    };
  },
};
</script>
<style >
.container {
  display: flex;
  justify-content: space-around;
}
</style>

页面效果:

接着我们开始引入插槽

在Category.vue中做改变,加<slot>标签

<template>
  <div class="category">
    <h3>{{title}}分类</h3>
    <!-- 定义一个插槽,等别的组件填充 -->
    <slot>我是插槽的默认值,当没有传递具体结构,我会出现</slot>
  </div>
</template>

<script>
export default {
  name: "Category",
  props:['title']
};
</script>

<style>
.category {
  background-color: aqua;
  width: 200px;
  height: 300px;
}
h3{
    text-align: center;
    background-color: burlywood;
}
</style>

在App.vue中使用<Category>时给插槽传入具体的结构和数据

<template>
  <div id="app" class="container">
    <Category title="中立阵营" :listData="zhongli">
      <img src="./static/image/zhongli.jpg" />
    </Category>
    <Category title="好人阵营" :listData="haoren">
      <ul>
        <li v-for="(item, index) in haoren" :key="index">{{ item }}</li>
      </ul>
    </Category>
    <Category title="狼人阵营" :listData="lang">
      <img src="./static/image/lang.jpg" />
    </Category>
  </div>
</template>

<script>
import Category from "./components/Category.vue";

export default {
  name: "App",
  components: {
    Category,
  },
  data() {
    return {
      zhongli: ["呆呆鸟", "猎鹰", "秃鹫", "鸽子", "鹈鹕"],
      haoren: [
        "加拿大鹅",
        "观鸟者",
        "鹅",
        "网红",
        "星界",
        "警长",
        "正义使者",
        "模仿",
        "侦探",
        "通灵",
      ],
      lang: [
        "刺客",
        "间谍",
        "食鸟鸭",
        "专业杀手",
        "承办丧葬者",
        "身份窃贼",
        "忍者",
        "爆炸王",
      ],
    };
  },
};
</script>
<style >
.container {
  display: flex;
  justify-content: space-around;
}
img{
 width:100%
}
</style>

页面效果:


2、具名插槽

在Category.vue中做改变,name属性给插槽起名字

<template>
  <div class="category">
    <h3>{{title}}分类</h3>
    <!-- 定义一个插槽,等别的组件填充 -->
    <slot name="center">我是插槽的默认值,当没有传递具体结构,我会出现1</slot>
    <slot name="footer">我是插槽的默认值,当没有传递具体结构,我会出现2</slot>
  </div>
</template>

在App.vue中,slot属性指定填充哪个插槽

<template>
  <div id="app" class="container">
    <Category title="中立阵营" :listData="zhongli">
      <img slot="center" src="./static/image/zhongli.jpg" />
      <a slot="footer" href="">点我查看更多</a>
    </Category>
    <Category title="好人阵营" :listData="haoren">
      <ul slot="center">
        <li v-for="(item, index) in haoren" :key="index">{{ item }}</li>
      </ul>
      <div slot="footer">
        <a href="">好人阵营</a>
        <h2>欢迎加入好人阵营</h2>
      </div>
    </Category>
    <Category title="狼人阵营" :listData="lang">
      <img slot="center" src="./static/image/lang.jpg" />
      <template slot="footer">
        <a href="">狼人阵营</a>
        <h4>欢迎来到我们狼人阵营</h4>
      </template>
    </Category>
  </div>
</template>

效果如下:

如果使用template包裹,可以有新的写法

    <!-- 旧的写法如下 -->
    <Category title="狼人阵营" :listData="lang">
      <img slot="center" src="./static/image/lang.jpg" />
      <template slot="footer">
        <a href="">狼人阵营</a>
        <h4>欢迎来到我们狼人阵营</h4>
      </template>
    </Category>

新的写法:注意v-slot只能写在组件的<template>

    <Category title="狼人阵营" :listData="lang">
      <img slot="center" src="./static/image/lang.jpg" />
      <template v-slot:footer>
        <a href="">狼人阵营</a>
        <h4>欢迎来到我们狼人阵营</h4>
      </template>
    </Category>

区别:


3、作用域插槽

数据和结构不在一块

数据放在Category.vue中

<template>
  <div class="category">
    <h3>{{title}}</h3>
    <!-- 定义一个插槽,等别的组件填充 sanxianshen会传给插槽的使用者-->
    <slot :sanxianshen="sanxianshen">我是插槽的默认内容</slot>
  </div>
</template>

<script>
export default {
  name: "Category",
  props:['title'],
  data(){
    return{
     sanxianshen: ["大天湿婆", "梵天", "毗湿奴"]
    }
  }
};
</script>

<style>
.category {
  background-color: aqua;
  width: 200px;
  height: 300px;
}
h3{
    text-align: center;
    background-color: burlywood;
}
</style>

在App.vue中接收到插槽传上来的数据。使用template包裹并且使用scope属性

<template>
  <div id="app" class="container">
    <Category title="印度三相神">
      <!-- 必须要用template包裹才能拿到插槽给的数据 -->
      <template scope="India">
        {{India}}
      </template>
      </Category>
  </div>
</template>

<script>
import Category from "./components/Category.vue";

export default {
  name: "App",
  components: {
    Category,
  },
  data() {
    return {}
  },
};
</script>
<style >
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
</style>

页面效果:

 在App.vue中展示数据的时候,使用不同的样式展示(无序列表,有序列表,p标签) 

<template>
  <div id="app" class="container">
    <Category title="印度三相神">
      <!-- 必须要用template包裹才能拿到插槽给的数据 -->
      <template scope="India">
        <ul>
          <li v-for="(item, index) in India.sanxianshen" :key="index">{{ item }}</li>
        </ul>
      </template>
    </Category>
    <Category title="印度三相神">
      <template scope="India">
        <ol>
          <li v-for="(item, index) in India.sanxianshen" :key="index">{{ item }}</li>
        </ol>
      </template>
    </Category>
    <Category title="印度三相神">
      <template scope="India">
        <p v-for="(item, index) in India.sanxianshen" :key="index">{{ item }}</p>
      </template>
    </Category>
  </div>
</template>

当然还可以使用ES6的解构赋值,看起来更好看

<template>
  <div id="app" class="container">
    <Category title="印度三相神">
      <!-- ES6解构赋值 -->
      <template scope="{sanxianshen}">
        <ul>
          <li v-for="(item, index) in sanxianshen" :key="index">{{ item }}</li>
        </ul>
      </template>
    </Category>
    <!-- 使用slot-scope,是新的api -->
    <Category title="印度三相神">
      <template slot-scope="{sanxianshen}">
        <ol>
          <li v-for="(item, index) in sanxianshen" :key="index">{{ item }}</li>
        </ol>
      </template>
    </Category>
  </div>
</template>

路由Vue Router

1、使用路由后有哪些变化

路由route是一组key-value的对应关系;多个路由需要经过路由器的管理

对应在vue中就是 路径与组件的映射成为路由

路由器router时时刻刻检测路径变化

vue-router是vue的一个插件库,专门用来实现spa应用(单页面web应用),即整个页面只有一个完成的页面。

vue-router相比之前的多页面(多个html)的区别

1.点击页面中的导航链接不会刷新页面

2.做页面的局部更新,数据需要通过ajax请求获取


2、引入路由器

npm install vue-router --save

后续可能会报错:vue_router__WEBPACK_IMPORTED_MODULE_2__.default is not a constructor

解决办法:卸载原本的vue-router,指定安装版本

npm uninstall vue-router
npm install vue-router@3

router/index.js创建路由器

新建router文件夹

在index.js中写入如下内容 

配置路由规则并创建路由实例

每个路由规则都是一个配置对象、其中至少包含path和component俩个属性

  • path表示 路由匹配的hash地址
  • component表示路由规则对应要展示的组件对象
//该文件专门用于创建整个应用的路由器
import VueRouter from "vue-router";

import Bedroom from "@/pages/Bedroom.vue";
import Kitchen from "@/pages/Kitchen.vue";

//创建并暴露一个路由器
export default new VueRouter({
  routes: [
    {
      path: "/bedroom",
      component: Bedroom,
    },
    {
      path: "/kitchen",
      component: Kitchen,
    },
  ],
});

main.js配置路由器

在main.js中添加内容如下

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入VueRouter
import VueRouter from 'vue-router'
//引入路由器
import router from './router'

//关闭vue的生产提示
Vue.config.productionTip = false

//应用插件
Vue.use(VueRouter);
new Vue({
  router:router,
  render: h => h(App),
}).$mount('#app')

为了能够让路由规则生效,必须把路由对象挂载到vue实例对象上


3、<router-link>声明式

router-link实现点击不同的导航,路径发生变化

<router-link>:该标签是一个vue-router中已经内置的组件,他会被渲染成一个<a>标签

router-link属性介绍
to:用于指定跳转的路径,to属性的值会被渲染为#开头的hash地址
tag:tag可以指定<router-link>之后渲染成什么组件,比如我们下面的代码会被渲染成一个<li>元素,而不是<a> 。 
<router-link to='/home' tag='li'>

<router-view>:该标签会根据当前的路径,动态渲染出不同的组件。

通过路由规则匹配到的组件,将会被渲染到<router-view>所在的位置

在App.vue中,修改为如下内容

<template>
  <div id="app">
    <div>
      <!--vue中借助router-link 实现路由的切换 -->
      <router-link to="/bedroom">卧室</router-link>
      <br />
      <router-link to="/kitchen">厨房</router-link>
      <!--路由匹配到的组件将渲染在这里-->
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
  components: {},
};
</script>

页面效果:点击卧室

页面效果:点击厨房


4、router.push编程式

点击 <router-link :to="..."> 等同于调用 router.push(...)

// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

注意:如果提供了 pathparams 会被忽略,上述例子中的 query 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 name 或手写完整的带有参数的 path

this.$router.push({

})


this.$router.replace({

})

this.$router.forward()//前进
this.$router.back()//后退
this.$router.go()

router.replace跟 router.push 不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

路由缓存

路由切走之后,再切回来。数据就没了,因为组件会销毁和重新挂载


5、$route

//$router : 是路由操作对象,只写对象

//$route : 路由信息对象,只读对象

//操作 路由跳转
this.$router.push({
      name:'hello',
      params:{
          name:'word',
          age:'11'
     }
})

params是路由的一部分,必须要在路由后面添加参数名。query是拼接在url后面的参数,没有也没关系

  1. 传参可以使用params和query两种方式。

6、路由传参params和query

使用params传参只能用name来引入路由,即push里面只能是name:’xxxx’,不能是path:’/xxx’

1.params 传参(显示参数)

{
 path: '/child/:id',
 component: Child
}
this.$router.push({
  path:'/child/${id}',
})

接收: this.$route.params.id

2. params传参(不显示参数)

通过路由的别名 name 进行传值的

{
 path: '/child,
 name: 'Child',
 component: Child
}
this.$router.push({
  name:'Child',
  params:{
   id:1
  }
})

接收: this.$route.params.id

query 传参(显示参数)

需要子路由提前配置好路由别名(name 属性)

{
 path: '/child,
 name: 'Child',
 component: Child
}
this.$router.push({
  name:'Child',
  query:{
   id:1
  }
})

接收: this.$route.query.id


7、路由器的2种工作模式

前端路由是基于hash值的变化进行实现的(比如点击页面中的菜单或者按钮改变URL的hash值,根据hash值的变化来控制组件的切换)

hash模式

路由器的默认工作模式是hash

1.对于一个url来说,什么是hash值?#及其后面的内容就是hash值。


2.hash值不会包含在HTTP请求中,即:hash值不会随着http请求发给服务器

3.hash模式

1.地址中永远带着#号,不美观。
2.若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
3.兼容性较好

history模式
1.地址干净,美观。
2.兼容性和hash模式相比略差。
3.应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。


8、嵌套路由

创建一个主路由
在src/router/index.js中配置嵌套路由,在Main.vue中设置子路由出口

const routes = [
    // 配置主路由
    {
        // 外层路由
        path:'/',
        component:Main,
        children:[
            // 嵌套路由/子路由:它们的路由出口在Main.vue中
            { path: 'home', component: Home },
            { path: 'user', component: User }
        ]
    }
]

9、路由重定向

路由重定向指的是:

  • 用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面;
  • 通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:
var myRouter = new VueRouter({
    //routes是路由规则数组
    routes: [
        //path设置为/表示页面最初始的地址 / ,redirect表示要被重定向的新地址,设置为一个路由即可
        { path:"/",redirect:"/user"},
        { path: "/user", component: User },
        { path: "/login", component: Login }
    ]
})

10、动态匹配路由

var User = { template:"<div>用户:{{$route.params.id}}</div>"}

var myRouter = new VueRouter({
    //routes是路由规则数组
    routes: [
        //通过/:参数名  的形式传递参数 
        { path: "/user/:id", component: User },
        
    ]
})

VueX 多组件共享数据

1、纯vue版实现求和案例

创建组件Count.vue

<template>
  <div>
    <h1>当前求和为:{{ sum }}</h1>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">当前求和为奇数再加</button>
    <button @click="incrementWait">等一等再加</button>
  </div>
</template>

<script>
export default {
  name: "Count",
  data() {
    return {
      n: 1, //用户选择的数字
      sum: 0, //当前的和
    };
  },
  methods: {
    increment() {
        this.sum+=this.n
    },
    decrement() {
        this.sum-=this.n
    },
    incrementOdd() {
        if(this.sum%2){
            this.sum+=this.n
        }
    },
    incrementWait() {
        setTimeout(() => {
            this.sum+=this.n
        }, 500);
    },
  },
};
</script>

<style>
button {
  margin-left: 5px;
}
</style>

在App.vue中引入Count.vue

<template>
  <div id="app">
    <count />
  </div>
</template>

<script>
import Count from "./components/Count.vue";

export default {
  name: "App",
  components: {
    Count,
  },
};
</script>

页面效果:

说实话,我写完觉得也没啥大的毛病啊,看看vuex能做什么优化呢


2、Vuex工作原理图

Vuex 是专门为 Vue.js 设计的状态管理库,它采用集中式存储管理应用的所有组件的状态。

我们常说的状态管理,往往是指外部管理,目的是对状态和组件进行分离,具体到 Vuex 里的表现就是:把应用的所有组件的状态抽取出来,以一个全局单例模式在应用外部采用集中式存储管理

工作对象

state:状态,用于存储对象数据
Actions:行为,用于保存方法的行为,可以包含异步操作
Mutations:转变,用于提交行为的结果,不可以包含异步操作
Getters:类似于在store中的计算属性
Modules:模块,将store分割成不同的模块,每个模块有自己的state、actions、mutations

store.dispatch

store.commit

state、Actions、Mutations这三个都要经过store管理

工作原理

Rendervue组件中可以读取state中的数据(对应工作原理图中的①这一条线)
Dispatch:调用store中的dispatch方法,由
vue组件派发给Actions执行,Actions可以继续给自身派发,也可以调用异步方法(Backend API)(对应工作原理图中的②这一条线)
Commit:调用store中的commit方法,由
Actions提交给Mutations执行,也可以直接由组件提交(对应工作原理图中的③⑤这两条线)
Mutate
ActionsMutations中更改state中的数据,不需要手动执行,由api直接调用。一般是Mutations调用,在此处调用,可以被开发者工具(Devtools)直接监控,由Actions调用时,不被监控。(对应工作原理图中的④⑥这两条线)


3、创建并引入store

因为我们使用的是vue2,对应就要安装vuex3

npm i vuex@3

创建store目录,并在其下创建index.js

index.js文件内容如下

import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//使用插件
Vue.use(Vuex)

//准备actions,用于响应组件中的动作
const actions={}

//准备mutations,用于操作数据
const mutations={}

//准备state,用于存储数据
const state={}

//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
})

============

在main.js引入vuex插件,并在创建vm时传入store

import Vue from 'vue'
import App from './App.vue'
//引入store
import store from './store'

//关闭vue的生产提示
Vue.config.productionTip = false

//创建vm
const vm=new Vue({
  store,
  render: h => h(App),
}).$mount('#app')
console.log(vm)

打开控制台就可以看到$store


4、使用Vuex对求和案例改造

改造store目录下的index.js

//该文件用于创建vuex中最为核心的store

import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//使用插件
Vue.use(Vuex)

//准备actions,用于响应组件中的动作
const actions={
    'jia':function(context,value){
      //context上下文对象,可以理解为mini版store
      console.log('actions中的jia被调用了',context,value)
      context.commit('JIA',value)
    },
    'jian':function(context,value){
        //context上下文对象,可以理解为mini版store
        console.log('actions中的jian被调用了',context,value)
        context.commit('JIAN',value)
      }
}

//准备mutations,用于操作数据
const mutations={
    'JIA':function(state,value){
        console.log('mutations中的JIA被调用了',state,value)
        state.sum+=value
      },
      'JIAN':function(state,value){
        console.log('mutations中的JIAN被调用了',state,value)
        state.sum-=value
      }
}

//准备state,用于存储数据
const state={
    sum:0
}

//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
})

======改造Conut.vue

<template>
  <div>
    <!--模板中可以直接使用vc中的数据,不用写this.-->
    <h1>当前求和为:{{ $store.state.sum }}</h1>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
    </select>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">当前求和为奇数再加</button>
    <button @click="incrementWait">等一等再加</button>
  </div>
</template>

<script>
export default {
  name: "Count",
  data() {
    return {
      n: 1//用户选择的数字
    };
  },
  methods: {
    increment() {
        this.$store.dispatch('jia',this.n)
    },
    decrement() {
        this.$store.dispatch('jian',this.n)
    },
    incrementOdd() {
        if(this.$store.state.sum%2){
            this.$store.dispatch('jia',this.n)
        }
    },
    incrementWait() {
        setTimeout(() => {
           this.$store.dispatch('jia',this.n)
        }, 500);
    },
  },
};
</script>

<style>
button {
  margin-left: 5px;
}
</style>

此时效果就和原来的一样了,每个按钮都生效

可以做优化,没有业务逻辑可以跳过actions,在组件里使用commit方法而不是dispatch


5、store中的getters配置项

当state中的数据需要经过加工后再使用,可以使用getters加工(适合逻辑复杂并需要被复用)

getters配置项 

在Count.vue中使用

<h1>当前求和为:{{ $store.state.sum }}</h1>    
<h1>当前求和放大10倍为:{{ $store.getters.bigSum }}</h1>


vue的ajax

1.vue的ajax

npm install axios

安装其他插件的时候,可以直接在 main.js 中引入并使用 Vue.use()来注册,但是 axios并不是vue插件,所以不能 使用Vue.use(),所以只能在每个需要发送请求的组件中即时引入。

怎么解决呢?在main.js中引用axios

import axios from 'axios';
Vue.prototype.$axios = axios //全局注册,使用方法为:this.$axios

执行 GET 请求

      this.$axios
        .get("/api/author/list", {
          params: {},
        })
        .then((response) => {
          console.log(response.data.data);
          this.nameArr = response.data.data;
        })
        .catch(function (error) {
          console.log(error);
        });

结果

----------------------

执行 POST 请求

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

1


2.引入element-ui

npm install element-ui -S

 官网:组件 | Element

在main.js入口文件使用element-ui插件

import Vue from 'vue'
import ElementUI from 'element-ui';                      // 引入element-ui
import 'element-ui/lib/theme-chalk/index.css';           // element-ui的css样式要单独引入
import App from './App.vue'

Vue.config.productionTip = false

Vue.use(ElementUI);   // 这种方式引入了ElementUI中所有的组件
new Vue({
  render: h => h(App),
}).$mount('#app')

在APP.vue组件中使用element-ui组件

<template>
  <div id="app">
    <HelloWorld />
     <!--图标-->
     <div>
      <el-row>
        <el-button>默认按钮</el-button>
        <el-button type="primary">主要按钮</el-button>
        <el-button type="success">成功按钮</el-button>
        <el-button type="info">信息按钮</el-button>
        <el-button type="warning">警告按钮</el-button>
        <el-button type="danger">危险按钮</el-button>
        <el-button type="primary" icon="el-icon-search">搜索</el-button>
      </el-row>
    </div>
  </div>
</template>

效果他不就来了嘛


3.网站部署

nginx是一款轻量级的web服务器,反向代理服务器。

将网站部署到nginx服务器上

vue打包命令,生成dist文件夹

npm run build

上传到服务器的html目录下

修改配置文件nginx.conf

效果:有页面,但是点击后并无数据

1


4.计算属性computed

vue认为data里写的就是属性,计算属性放在computed

计算属性要求把计算的整个过程配置成一个对象

get的return返回值是什么,计算属性的值就是什么

对App.vue做内容修改

<template>
  <div id="app">
    <div>
      姓: <input type="text" v-model="firstname" /> <br />
      名: <input type="text" v-model="lastname" /> <br />
      全名: <span>{{ fullname }}</span>
    </div>
  </div>
</template>
 
<script>
export default {
  name: "App",
  components: {},
  data() {
    return {
      firstname: "小",
      lastname: "羽毛",
    };
  },
  computed: {
    // 是属性fullname通过data里的属性计算出来的
    fullname: {
      get() {
        console.log('get被调用了'); //此处this是vc
        return this.firstname + "-" + this.lastname;
      },
    },
  },
};
</script>   

界面效果:

打开vue开发者工具,就能看到哪些是计算属性了

 再看看这个效果,猜猜get被调用了几次,由于computed的缓存机制,get方法只被调用了一次

get方法调用时机总结:

1.初次读取fullname时,get会被调用。

2.所依赖的数据发生变化时,get会被调用。

依赖指的就是它是通过谁计算出来的,它就依赖谁。

set方法是可以不写的,在计算属性发生改变时会调set方法,入参就是改变后的值

  computed: {
    // 是属性fullname通过data里的属性计算出来的
    fullname: {
      get() {
        console.log('get被调用了'); //此处this是vc
        return this.firstname + "-" + this.lastname;
      },
      set(value){
      //value是改变后的值
      console.log('set被调用了',value)
      const arr=value.split('-')
      this.firstname=arr[0]
      this.lastname=arr[1]
      }
    },
  },

计算属性简写形式如下,

要求只有computed里只有get,没有set修改

原来的计算属性:

简写后

  computed: {
    // 是属性fullname通过data里的属性计算出来的
    fullname() {
      return this.firstname + "-" + this.lastname;
    },
  },

1


部署前后端分离项目

1、npm run build

npm run build——此命令从 package.json 脚本字段运行构建字段,结果构建出来dist文件夹

dist 文件夹就是我们需要的包,随后放至服务器部署上线即可;需要注意打包之后无论在项目中做了何种修改,都需要 npm run build 重新打包。


2、前后端一起部署

前端打包成静态文件后,拷贝到后端项目中,把静态资源文件放到static目录下。

部署后端项目

使用maven,package打包

打包结果

在命令行窗口运行我们的jar包

java -jar E:\workspace\mybatis-plus\springboot-project\target\spingboot-project-1.0-SNAPSHOT.jar

http://localhost:8080/index.html


3、 Nginx 部署

前端用 Nginx 部署,后端用 Nginx 做代理

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值