Vue进阶

147 阅读7分钟

1.Vue中组件的使用

1-1.全局组件和局部组件

<body>
    <div id="app">
        <h1>{{msg}}</h1>
        <!--登录-->
        <login></login>
        <!--注册-->
        <register></register>
        <!--用户添加-->
        <add></add>
    </div>
</body>
</html>
<script src="js/axios.min.js"></script>
<script src="js/vue.js"></script>
<script>
    //1.定义一个全局组件:参数1:组件名称;参数2:组件的配置对象
    Vue.component('login',{
        template:`<div><h2>用户登录</h2> <form action=""></form></div>`,//用来书写该组件的html代码
    });
    Vue.component('register',{
        template:`<div><h2>用户注册</h2>  <form action=""></form></div>`
    });
    const app = new Vue({
        el:"#app",
        data:{
            msg:"Vue中组件的使用"
        },
        methods:{},
        computed:{},
        components:{ //注册局部组件
            add:{    //添加局部组件
                template:`<div><h2>用户添加</h2> <form action=""></form></div>`
            }
        }
    });
</script>

1-2.Vue的组件中定义组件数据

  • 组件中可以定义:data、methods、computed、components
  • P24-27:50,向子组件传递一个动态数据,双向绑定。P24-34:00,单向数据流。P25-20:00,通过子组件传递数据到父组件。

2.Vue-router插件的使用

Vue_router的基本使用.png

2-1.vue-router之在js代码中切换路由

截图_20212820102805.png

2-2.vue-router的基本使用之参数传递

<body>
    <div id="app">
        <h1>{{msg}}</h1>
<!--标签形式切换路由
    地址栏传递参数分为两种方式:
       1.queryString ?  this.$route.query.key
       2.restful:路径传递参数 /xx/21 this.$route.params.key
-->
        <a href="#/login?name=jun&password=123">用户登录</a>
        <a href="#/register/21/jun">用户注册</a>
        <!--queryString的方式-->
        <router-link to="/login?name=jun&password=123">用户登录</router-link>
        <router-link :to="{path:'/login',query:{name:'jrh',password:1236}}">用户登录</router-link>
        <router-link :to="{name:'Login',query:{name:'jrh',password:123}}">用户登录</router-link>
        <!--restful-->
        <router-link :to="{path:'/register/22/junhr'}">用户注册</router-link>
        <router-link :to="{name:'Register',params:{id:23,name:'jun'}}">用户注册</router-link>
        <!--展示路由组件标签-->
        <router-view/>
    </div>
</body>
</html>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
    //login
    const login =  {
        template:`<div><h4>用户登录</h4></div>`,
        created(){
            console.log("created");
            console.log(this.$route);//获取当前路由对象
            console.log(this.$route.query.name);
            console.log(this.$route.query.password);
        }
    };
    //register
    const register =  {
        template:`<div><h4>用户注册</h4></div>`,
        created(){
            console.log(this.$route);
            console.log(this.$route.params.id); //获取路径传参的参数
            console.log(this.$route.params.name);
        }
    };
    //1.创建路由规则对象
    const router = new VueRouter({
        routes:[
            //name:这个属性代表路由对象名称  用来给路由对象一个唯一名称标识
            {path:'/login',component:login,name:'Login'},
            {path:'/register/:id/:name',component:register,name:'Register'},
        ]
    });
    const app = new Vue({
        el: "#app",
        data: {
            msg:"vue router 基本使用之参数传递"
        },
        methods:{},
        router,//注册路由对象
    });
</script>

2-3.嵌套路由

  • P30-24:02,vue-rooter中嵌套路由的使用。

3.Vue-cli脚手架

3-1.安装nodejs

  • 安装nodejs:配置环境变量 =》 最终在cmd中输入 node -v 可以查看到版本信息即配置成功。Nodejs 提供了npm命令(node package manager) 即nodejs包管理工具。
  • 设置nodejs本地仓库:
  1. npm config set cache "E:\DevProgram\nodejs\nodereps"
  2. npm config set prefix "E:\DevProgram\nodejs\nodereps"
  • 设置淘宝镜像:npm config set registry https://registry.npm.taobao.org 查看镜像:npm config get registry
  • 验证nodejs环境配置:npm config ls

3-2.安装vue-cli

  • 安装:npm install -g vue-cli
  • 配置nodejs本地仓库环境变量:E:\DevProgram\nodejs\nodereps
  • 测试命令是否能够执行:vue init

3-3.使用vue-cli创建项目

  • 通过cli创建项目:vue init webpack 项目名 根据提示一路按enter回车,直到:`? Use ESLint to lint your code?(Y/n) 可以选择no,是否建立单元测试?no,是否建立Nightwatch测试?no 。最后是否使用npm管理,回车。
  • 运行这个创建好的项目(比如是hellovue项目):cd hellovue ,然后以开发模式运行:npm run dev
  • 最后浏览器访问该项目:http://localhost:8080

3-4.vue-cli项目目录结构说明

  • P34-26:00,src目录的讲解。
  • node_modules 在项目提交到GitHub上时,可以排除。因为里面东西太多,而且别人在部署上传的项目时可以根据package.json自己下载(执行npm install命令时,自动下载)。
  • 自己安装依赖:比如安装axios依赖:进入到项目文件夹,运行cmd:比如:E:\hellovue>npm install axios --save (--save表示保存的意思) 截图_20214020024052.png
  • import HelloVue from '@/components/HelloVue' 这里的@就相当于src

3-5.测试

  • <style scoped 如果style标签中加入了scoped,这个属性代表这个style里面的所有样式自己组件可用;如果没有加scoped属性,则代表这个style样式影响全部组件。

4.标准前端开发

  1. 一般开发中components文件夹中放的是一些公共的组件,src下新建一个文件夹views业务组件
  2. 在组件的声明中写成:component: () => import('../views/Login') 代替原来的:
//import Login from "../views/Login";
export default new Router {
    routes:[
        path:'/login',
        name:'Login',
        //component: Login
        component: ()=>import('../views/Login') //按需加载(用到的时候才加载)
    ]
}
  1. 一般在发送axios请求前需要安装axios:npm install axios --save,安装完成后,引入axios:import axiox from 'axios' ,发送axios请求:axios.get("http://localhost:8989/users"),但是这样不解耦。解决办法是:在src目录下创建一个utils目录,在目录里创建一个request.js ,之后发送请求的时候直接写:instance.get("/users")
import axios from 'axios'  //引入axios
//创建默认的实例
const instance = axios.create({
    baseURL: 'http://localhost:8989',
    timeout: 5000,
  }
);
//请求拦截器
instance.interceptors.request.use(config => {
  console.log("请求拦截器");
  return config;
});
//响应拦截器
instance.interceptors.response.use(response => {
  console.log("响应拦截器");
  return response;
},err => {
  console.log("响应出现错误时进入的拦截器");
});
//暴露instance实例对象
export default instance;

5.Vue-cli标准开发方式结合后端实现

  • P36-29:14,地址栏输入非法,跳转到404页面。P37-30:00,遇到了:1.vue封装axios的post方法的跨域问题2.新建一个config类实现WebMvcConfigurer解决跨域问题CORS.png
  • 用户登录页面:输入用户名和密码后,向后端的接口发送登录请求,根据输入的信息,查询数据库,然后后端“告诉”前端是否登录成功。在后端生成一个token,同时将token保存到后端的redis里。
  • 启动虚拟机,输入:docker run -d --name redis01 -p 6379:6379 redis:5.0.14
  • P37-36:58,登录成功后,显示欢迎xxx,<span v-show="admin.username">欢迎: {{admin.username}}</span>,进入员工管理系统主页。P37-45:00,解决同一个路由多次切换报错的问题。
  • P38-5:17,当首次点员工管理按钮时,就会发送一个查询所有员工的请求。

5-1.登录功能实现

后端AdminServiceImpl.java

@Service("adminService")
@Transactional
public class AdminServiceImpl implements AdminService {
    @Resource
    private AdminDao adminDao;

    //管理员登录方法的实现:P37-14:45
    @Override
    @Transactional(propagation = Propagation.SUPPORTS) //查询操作建议声明为SUPPORTS
    public Admin login(Admin admin) {
        Admin adminDB = adminDao.findByUserName(admin.getUsername());
        if(adminDB == null) throw new RuntimeException("用户名输入错误!");
        if(!adminDB.getPassword().equals(admin.getPassword())) throw new RuntimeException("密码输入错误!");
        return adminDB;
    }

    @Override
    public Admin queryById(Integer id) {
        return this.adminDao.queryById(id);
    }
}    

后端AdminController.java

@RestController
//@RequestMapping("admin")
@CrossOrigin(origins="*",allowCredentials = "true",allowedHeaders = "",methods = {})  //解决前端跨域的问题
public class AdminController {
    private static final Logger log = LoggerFactory.getLogger(AdminController.class);
    @Resource
    private AdminService adminService;
    @Autowired  //@Resource
    private RedisTemplate redisTemplate;

    //退出用户登录
    @DeleteMapping("/token")
    public void token(String token) {
        redisTemplate.delete(RedisPrefix.TOKEN + token);
    }

    //已登录的用户信息
    @GetMapping("/token")
    public Admin admin(String token) {
        log.info("接收的token信息:{}",token);
        return (Admin) redisTemplate.opsForValue().get(RedisPrefix.TOKEN + token);
    }

    //验证前端发过来的登录信息
    @PostMapping("login")
    public Map<String,Object> login(@RequestBody Admin admin, HttpSession session) { //前端传过来的是JSON格式的数据
        log.info("admin-username:{},admin-password:{}",admin.getUsername(),admin.getPassword());
        Map<String,Object> result = new HashMap<>();
        //进行登录操作。
        try {
            //登录成功,记录登录的标记在服务器端,并给前端系统响应token。P37-17:34
            Admin adminDB = adminService.login(admin);
/**
 * 生成token信息,可以拿本次请求的session的id作为通信信息P37-17:50。
 *  然后将token信息与adminDB 记录放到redis里面。整合redis。P37-18:26
 *  P37-20:38,redis默认的序列化方式是对象的序列化,需要配置成JSON序列化,新建一个config包
 * [root@192 ~]# docker run -d --name redis01 -p 6379:6379 redis:5.0.14
 * d42e48458056ed9262ee5e6e1e2ce9ff2c2197cea8451f6f721468f6a3bb6cf4
 */
            String token = session.getId(); //生成token信息。
            //把生成的token信息放入redis,P37-22:52 , 最后两个参数表示存30分钟
            redisTemplate.opsForValue().set(RedisPrefix.TOKEN+token,adminDB,30, TimeUnit.MINUTES);
            //将token信息传到前端
            result.put("success", true); //标记是正确还是失败
            result.put("token", token);
        } catch (Exception e) {
            e.printStackTrace();
            //登录失败
            result.put("success", false);
            result.put("msg", e.getMessage());
        }
        return result;
    }
}    

5-2.员工页面展示

后端EmpController.java

@RestController
@CrossOrigin //解决跨域问题
public class EmpController {
    private static final Logger log = LoggerFactory.getLogger(EmpController.class);
    @Resource
    private EmpService empService;
    //1.根据id查询员工信息
    //@GetMapping("/emps/{id}")
    //public ResponseEntity<Emp> queryById(@PathVariable("id") Integer id) {
    @GetMapping("/emp")
    public ResponseEntity<Emp> queryById(Integer id) {
        return ResponseEntity.ok(this.empService.queryById(id));
    }
    
    //2.查询所有员工信息的方法
    @GetMapping("/emps")
    public List<Emp> emps() {
        List<Emp> emps = empService.queryAll();
        return emps;
    }

    //3.根据id删除员工信息
    //@DeleteMapping("/emps/{id}")
    //public ResponseEntity<Boolean> deleteById(@PathVariable("id") Integer id) {
    @DeleteMapping("/emp")
    public ResponseEntity<Boolean> deleteById(Integer id) {
        log.info("要删除的员工id是:{}",id);
        return ResponseEntity.ok(this.empService.deleteById(id));
    }

    //4.添加或者保存员工信息 (如果前端传来的数据中有id,则说明是修改操作;如果没有id,则是添加操作)
    @PostMapping("/emp")  //前端传过来的是JSON格式的对象,需要把JSON转换成后端所接受的对象@RequestBody
    public ResponseEntity<Emp> update(@RequestBody Emp emp) {
        //利用Spring中的一个工具类。StringUtils
        if (StringUtils.isEmpty(emp.getId())) { //添加操作
            log.info("当前保存的员工信息:{}"+emp.toString());
            return ResponseEntity.ok(this.empService.insert(emp));
        } else {  //修改操作
            log.info("当前修改的员工信息:{}"+emp.toString());
            return ResponseEntity.ok(this.empService.update(emp));
        }
    }
}

5-3.Vue-cli项目部署

方案一

  • 在项目根目录中执行命令:npm run build 【注意】:vue脚手架打包的项目必须在服务器上运行不能直接双击运行。打包完成后为dist文件夹。
  • 将dist文件夹直接通过docker部署到nginx里面,前端系统一般不建议部署到tomcat里面,因为tomcat属于动态服务器。直接启动nginx是不行的:docker run -d --name nginx01 -p 80:80 nginx:1.19.10,启动nginx时,一定要把HTML目录映射,让其文件夹中装的是需要部署的dist文件,先进入普通启动的nginx里:docker exec -it(交互的方式) nginx01 bash 然后切换到nginx的家目录:cd /usr/share/nginx/html,日后只需要映射到这个目录就行,将需要部署的dist文件上传到/root目录P41-17:09
[root@192 ~]# mkdir html
[root@192 ~]# ls
dist  dockerfile  ems  html  redisConf  redisData  tomcat-8.0.tar
[root@192 ~]# mv dist/ html/
[root@192 ~]# docker run -d -p 80:80 --name nginx01 -v /root/html/dist:/usr/share/nginx/html nginx:1.19.10
  • 按上述方法启动完nginx后,在地址栏输入:http://192.168.200.130/index.html就能访问到部署的项目页面。但目前还无法和后端交互。

方案二

Vue_cli部署.png

  • P41-18:34P42-3:07docker run -d -p 80:80 --name nginx01 -v /root/html/:/usr/share/nginx/html nginx:1.19.10。地址栏访问:http://192.168.200.130/dist/index.html

6.Vuex-官方状态管理器

  1. 在vue-cli项目中安装vuex:npm install vuex --save,新建一个文件夹P43-20:00:store(代表存储状态的意思),在store文件夹下新建一个入口文件:index.js
  2. 配置Vuex
  • 在stroe中创建index.js文件
import Vue from 'vue'
import Vuex from 'vuex'
//1.安装vuex
Vue.use(Vuex);
//2.创建store对象
const store = new Vuex.Store({});
//3.暴露store对象
export default store;
  • 在main.js中引入stroe并注册到vue实例
import Vue from 'vue'
import App from './App'
import router from './router'
import store from "./stroe";//引入store
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>',
  store,//注册状态
})

6-1.state属性

//1.state属性
//作用: 用来全局定义一些共享的数据状态
//2.语法
const store = new Vuex.Store({
    state:{
        counter:0,//定义共享状态
    },
}
//3.使用
{{$store.state.counter}} ===> {{this.$store.state.counter}}

6-2.mutations属性

//1.mutations 属性
//作用: 用来定义对共享的数据修改的一系列函数
//2.语法
const store = new Vuex.Store({
  state:{
    counter:0,//定义共享状态
  },
  mutations:{
    //增加
    increment(state){
      state.counter++
    },
    //减小
    decrement(state){
      state.counter--
    }
  }
});

//3.使用
this.$store.commit('decrement');
this.$store.commit('increment');

//4.mutations传递参数
//a.定义带有参数的函数
mutations:{
//addCount 参数1:state 对象 参数2:自定义参数
   addCount(state,counter){
        console.log(counter);
        return  state.counter += counter ;
  }
}
//b.调用时传递参数
this.$store.commit('addCount',11);

6-3.getters属性

  • 官方: 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
  • 作用: 用来定义对共享的数据的计算相关的一系列函数 相当于 computed 属性 会对结果进行缓存
getters:{
    //平方
    mathSqrts(state){
        console.log("--------");
        return state.counter*state.counter;
    },
    //乘以一个数字
    mathSqrtsNumber(state,getters){
        return getters.mathSqrts*3;
    },
    //传递参数
    mathSqrtsNumbers(state,getters){
        return function (number){
        return  number;
        }
    }
}
# 3.使用
- 1.{{$store.getters.mathSqrts}}
- 2.{{$store.getters.mathSqrtsNumber}}
- 3.{{$store.getters.mathSqrtsNumbers(3)}}