VUE培训

一.Before

目的:熟悉一些基本概念,开发流程,常用组件,快速的上手写项目,先run起来

产出:一个模拟购物车的小模块

二.项目的搭建

1.下载安装node.js

https://nodejs.org/en/
选择 LTS(长期支持的版本) 版本,下载安装(一直点下一步)
安装完毕之后,在命令行下验证是否安装成功:输入nmp,显示如下就表示安装成功

image.png

2.安装cnpm (在天朝你懂的)
npm install -g cnpm --registry=https://registry.npm.taobao.org
3.安装 vue-cli (一个命令行工具,可用于快速搭建大型单页应用)
cnpm install -g vue-cli
4.新建一个项目

先创建一个你心仪的文件夹,然后在该文件夹下打开命令行

vue init webpack 你的项目名
image.png

这里会询问你安装一些组件,暂时我们只安装vue-router

5.安装项目的依赖包

加入刚刚创建项目的根目录,在根目录下打开命令行

cnpm install
6.打开项目测试一下(如果端口被占用可以在根目录下config/index.js中修改dev对象的port属性)
npm run dev
7. 项目结构的说明
|-- build                            // 项目构建(webpack)相关代码
|   |-- build.js                     // 生产环境构建代码
|   |-- check-version.js             // 检查node、npm等版本
|   |-- dev-client.js                // 热重载相关
|   |-- dev-server.js                // 构建本地服务器
|   |-- utils.js                     // 构建工具相关
|   |-- webpack.base.conf.js         // webpack基础配置
|   |-- webpack.dev.conf.js          // webpack开发环境配置
|   |-- webpack.prod.conf.js         // webpack生产环境配置
|-- config                           // 项目开发环境配置
|   |-- dev.env.js                   // 开发环境变量
|   |-- index.js                     // 项目一些配置变量
|   |-- prod.env.js                  // 生产环境变量
|   |-- test.env.js                  // 测试环境变量
|-- src                              // 源码目录
|   |-- components                     // vue公共组件
|   |-- store                          // vuex的状态管理
|   |-- App.vue                        // 页面入口文件
|   |-- main.js                        // 程序入口文件,加载各种公共组件
|-- static                           // 静态文件,比如一些图片,json数据等
|   |-- data                           // 群聊分析得到的数据用于数据可视化
|-- .babelrc                         // ES6语法编译配置
|-- .editorconfig                    // 定义代码格式
|-- .gitignore                       // git上传需要忽略的文件格式
|-- README.md                        // 项目说明
|-- favicon.ico 
|-- index.html                       // 入口页面
|-- package.json                     // 项目基本信息

三.基本知识

1.概念:数据驱动的组件式编程

  传统的前端开发模式:

  拼界面->找到dom节点->修改属性->检测是否有其他影响的节点->根据刚刚修改的dom节点更新自己的状态

  数据驱动 :

  只要专注于数据的状态,无需理会UI更新问题 , 不管是样式还是内容,可见性还是切换class,都只需更改数据
<div id="app">
  {{ msg }}
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
    const app = new Vue({
      el: '#app',
      data: {
        msg: 'Hello Vue!'
      }
    })
</script>

2.组件式编程

1.如下图所示,如搭积木一样一个个组装组件,就形成了我们整个应用


image.png

2.组件之间的通信,一句话,父组件通过prop把值传给子组件,子组件用


image.png
<html>

<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
</head>

<div id="app">
    <h2>props演示
        <h2>
            <input v-model="parentMsg" />
            <child :my-message="parentMsg"></child>
        <h2>$emit,$on演示</h2>

        <div id="counter-event-example">
            <p>总数:{{ total }}</p>
            <p>子组件触发事件携带的数据,子组件当前的count值: {{ payload }}</p>
            <button-counter v-on:increment="incrementTotal"></button-counter>
            <button-counter v-on:increment="incrementTotal"></button-counter>
        </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>

<script>
    Vue.component('child', {
        props: ['myMessage', 'count'],
        template: '<p><font>这是从父组件传过来的值:{{ myMessage }}</font></p>'
    })

    Vue.component('button-counter', {
        template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
        data() {
            return {
                counter: 0
            }
        },
        methods: {
            incrementCounter: function () {
                this.counter += 1
                this.$emit('increment',{ message:this.counter })
            }
        },
    })

    Vue.component('slotParent',{
        template: '<div><h5>我是父组件的标题</h5><p></p><div>'
    })

    new Vue({
        el: '#app',
        data: {
            parentMsg: 'Message from parent',
            total: 0,
            payload:''
        },
        methods: {
            incrementTotal: function (payload) {
                this.total += 1
                this.payload = payload.message
            }
        }
    })
</script>

3.插槽(内容分发)
将父组件的内容加到子组件里面叫内容分发

3.一些常用指令

image.png

(1)v-text: 用于更新绑定元素中的内容,类似于jQuery的text()方法
(2)v-html: 用于更新绑定元素中的html内容,类似于jQuery的html()方法
(3)v-if: 用于根据表达式的值的真假条件渲染元素
(4)v-show: 用于根据表达式的值的真假条件显示隐藏元素,切换元素的 display CSS 属性
(5)v-for: 用于遍历数据渲染元素或模板
(6)v-on: 用于在元素上绑定事件,可以简写为@,例如 <button @click=""></button>
(5)v-bind:用于在元素上绑定属性,例如class或者style,可以简写为 :class :style
v-show和v-if的区别,v-show只是控制标签显示隐藏,而v-if通过逻辑判断控制输出,优先使用v-if


<div id="app">
    <h2>if演示</h2>
    <p v-if="flag === 'if'">if content</p>
    <!-- 注意v-else-if一定要紧跟着v-if或者 v-else-if -->
    <p v-else-if="flag === 'elseif'">elseif content</p>
    <!-- 注意v-else一定要紧跟着v-else-if -->
    <p v-else>else content</p>
    <p v-show="isshow">aaaaaaa<p>
    
    <h2>v-for 用于遍历数组,对象</h2>
    <h5>遍历数组</h5>
    <ul v-for="fruit in list">
        {{fruit}}
    </li>
    </ul> 
    <h5>遍历对象</h5>
    <ul v-for="(value,key) in object">
            <!-- {{key}} : {{value}} -->
            <span v-text="key"></span>
            <span v-text="value"></span>
        </li>
    </ul>

    <h2>v-model 用于绑定表单元素</h2>
    <input type="text" v-model="username" />

    <input type="checkbox" v-model="checked" />
    <h2>v-on 用于绑定事件 可以简写为@ </h2>
    <button @click="showPrompt">点我</button>

    <h2>v-html 渲染html代码</h2>
    <div>
            {{ htmlTxt }}
    </div>
    <div v-html="htmlTxt">

    </div>

    <h2>v-bind 用户绑定元素的属性(class,style等) 可以简写为 :</h2>
    <!-- <a :href="www.baidu.com"></a> -->
    <h5>
       <span :class="clazz"><font></font></span>
       <span :style="styleFont">文字</span>
    </h5>
</div>

<script src="vue.js"></script>
<script>
    const app = new Vue({
      el: '#app',
      data: {
        msg: 'Hello Vue!',
        flag: 'xxx',
        isshow: false,
        list:['apple','orange','banana'],
        object: {
            name:'apple',
            price:'12',
            sold:'10'  
        },
        username: '123',
        checked: true,
        htmlTxt:'<p><font style="color:red">这是渲染的html代码</font></p>',
        hasAAA:true,
        hasBBB:false,
        clazz:{
            error : true,
            success : false
        },
        styleFont:{
            color:'red',
            fontSize:'13px',
        }
      },
      methods:{
          showPrompt(){
              alert("123");
          }
      }
    })
</script>

4.计算属性&监听器&方法&过滤器

1.计算属性:如果在模板里面存在复杂的运算,例如

{{ message.replace('AAA','').reverse()}}

建议用计算属性处理

computed:{
    procMessage(){
        return  message.replace('AAA','').reverse();
   }
}
//而在模板上则替换为
{{ procMessage }}

2.侦听器(watch)
例如数据a改变后其他数据(例如数据b)需要跟着改变,则我们就用watch里面侦听数据a

watch {
    // 传入的newValue 则是改完的值
    a(newValue){
       this.b = '123'
    }
}

3.过滤器,顾名思义,主要 是用于文本的格式化,只能用于{{ }} 和 v-bind

 {{ message | removeA | removeB }}
或者
<span :msg='removeA | removeB'>

filters:{
   removeA(val){
       return val.replace('AAA','')
   },
  removeB(val){
      return val.replace('BBB','')
  }
}

4.方法methods

  <button @click='func()'></button>

methods:{
    func(){
      alert('clicked')
    }
}

5.需要注意的点:
1.计算属性与方法的异同就是,计算属性只要依赖不发生改变,他就直接返回之前计算的结果,不重新计算, 而方法每一次都会运行一次
2.侦听器一般来说处理耗资源的操作,比如异步请求,建议优先考虑使用计算属性来处理

6.代码示例

<div id="app">
    <!-- 计算属性与方法的异同就是,计算属性只要依赖不发生改变,他就直接返回之前计算的结果,不重新计算,而方法每一次都会运行一次 -->
       <span v-text="procMsg()"></span>
       <span v-text="procMsg()"></span>

    <!-- watch监听 数据需要随着其它数据变动而变动 -->
    <input v-model='msg2' />

    {{ fullName | removeIllegalChar }}
    
</div>


<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
     new Vue({
         'el':'#app',
         computed:{
            handleMsg(){
                // console.log("正在执行计算")
                return this.msg + '哈哈哈'
            }             
         },
         methods:{
            procMsg(){
                // console.log("正在执行方法")
                return this.msg + '哈哈哈';
            }
         },
         data:{
             msg:'ray',
             msg2:'ray',
             fullName:'rayaaabb'
         },
         watch:{
             msg(val){
                 this.fullName = val + 'bbb'
             }
         },
         filters:{
            removeIllegalChar(val){
                 return val.replace('aaa','');
             }
         }
     })
</script>
  

5组件的组合,插槽(slot)

1.内容分发
父组件将内容
2.单个插槽
除非子组件模板包含至少一个 <slot> 插口,否则父组件的内容将会被丢弃。当子组件模板只有一个没有属性的插槽时,父组件传入的整个内容片段将插入到插槽所在的 DOM 位置,并替换掉插槽标签本身。
最初在 <slot> 标签中的任何内容都被视为备用内容。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。
3.具名插槽

4.作用域插槽

<div id="app">
    <!-- 单个插槽 -->
    <single-slot></single-slot>
    <!-- 具名插槽 -->
    <name-slot></name-slot>
    <!-- 作用域插槽 -->
    <myscopeslot></myscopeslot>
</div>
<script src="vue.js"></script>
<script>
    Vue.component('single-slot', {
        template: `<div>
           <h2>我是父組件的標題<h2>
           <child>

           </child>
        </div>`,
        components: {
            child: {
                template: `
                    <div>
                        <slot>
                            如果父組件沒有內容傳給我,我就會顯示
                        </slot>  
                    </div>
                `,

            }

        }
    })

    Vue.component('name-slot', {
        template: `
        <div>
            <h2>我是父组件</h2>
            <child>           
                <p slot="one">我是父组件,但是我要加入name为one的插槽中</p>
                <p slot="two">我是父组件,但是我要加入name为tow的插槽中</p>
                <p>我是父组件,但是我要加入默认插槽中</p>
            </child>
        </div>
        `,
        components: {
            child: {
                template: `<div>
                    <slot name="one"></slot>
                    <slot name="two"></slot>
                    <slot ></slot>
                </div>
            `,
            }

        }
    })



    Vue.component('myscopeslot', {
        template: `
            <div>
                <child :items=fruits>
                        <template slot-scope="props" slot="scopeslot">
                            <span>{{ fruits[props.indexCount].name }}</span>
                            <span>{{ fruits[props.indexCount].price }}</span>
                            <span>{{ fruits[props.indexCount].sold }}</span>
                        </template>
                </child>
            </div>
        `,
        data() {
            return {
                fruits: [{
                        name: '苹果',
                        price: 10,
                        sold: 5
                    },
                    {
                        name: '梨子',
                        price: 10,
                        sold: 5
                    },
                    {
                        name: '香蕉',
                        price: 10,
                        sold: 5
                    }
                ]
            }
        },
        components: {
            child: {
                props: ['items'],
                template: `
                <div>
                    <ul v-for="(item, index) in items">
                        <li>
                        <slot name="scopeslot" :indexCount=index></slot>
                        </li>
                    </ul>
                </div>
                `
            }
        }
    })

    new Vue({
        "el": "#app"
    })
</script>

四.实战

1.安装引入element-ui

命令行安装

npm install element-ui -S

引入

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'
import Vue from 'vue'
Vue.use(ElementUI)

2.编写一个简单的登录功能

1.在components创建一个Login.vue
2.先搭建一个单页面组件的框架,

<template></template> 写html代码,注意template标签内一定要有一个html标签包裹所写的内容,例如下面的例子就用<div id="login">包裹住
<script></script> 编写脚本
<style></style> 样式
用element-ui组件先编写一个基本的页面

<template>
    <div id="login">
        <el-form :model="loginForm">         
            <h2>Ray商城登录</h2>
            <el-form-item>
                <el-input name="username" placeholder="请输入用户名/手机号" auto-complete="on"></el-input>
            </el-form-item>

            <el-form-item>
                <el-input  name="password" placeholder="请输入密码" auto-complete="on"></el-input>
            </el-form-item>

            <el-form-item>
                <el-button type="primary">登录</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>


<script>
export default {
  name: "#login",
  data() {
    return {

    }
  },
  methods: {
  }
};
</script>
<style scoped>
.el-input {
        width:300px;
}    
</style>
3.我们给form,两个input框绑定数据,给表单控件绑定用v-model
<el-input v-model="loginForm.username" name="username" placeholder="请输入用户名/手机号" auto-complete="on"></el-input>
<el-input v-model="loginForm.password" name="password" placeholder="请输入密码" auto-complete="on"></el-input>

在data方法里面,同样也要创建数据模型

loginForm: {
      username: '',
      password: '',
 }
4.表单验证

element-ui 里面有表单校验的例子
在data() 里面加上这个loginRules,用于表单验证

loginRules: {
        username: [
            {
                required: true,

                message: "请输入用户名",

                trigger: "blur"
            }
        ],
        password: [
           {
               required: true,
               message: '请输入密码',
               trigger: 'blur'
           }
        ]
      }

与组件的绑定

<el-form  :rules="loginRules">

<el-form-item prop="username">

<el-form-item prop="username">
5.请求远程服务器登录

请求服务器要用axios

npm install axios --save

在src目录下创建utils文件夹,然后创建request.js文件

//直接引入
import axios from 'axios'

//因为axios默认发的是json格式数据,我们要做表单提交,需要更改axios配置
// 引入 Qs是为了把json格式,转为formdata 的数据格式
import Qs from 'Qs'
const service = axios.create({
    baseURL: 'api',
    timeout: 1000,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    transformRequest: [function (data) {
        // 对 data 进行任意转换处理
        return Qs.stringify(data);
    }],
});

创建好request.js的工具类后,建议在src下创建apis文件夹,用于处理服务器请求
在apis下创建login.js,用于定义登录所需的请求服务器接口

import request from '@/utils/request'
export function loginReq(username,password) {
    return request.post('/login.json',{
        loginName: username,
        loginPassword: password
    })
}

创建好了之后,可以在Login.vue上调用loginReq接口

//先引入接口
import { loginReq } from '@/apis/Login'
//在methods加上处理方法
methods: {
      handleLogin (){
           this.$refs.loginForm.validate((valid) => {
               if(valid){
                  loginReq(this.loginForm.username,this.loginForm.password).then((res) => {
                      console.log(res.data.status);
                  })
               }else{
                   console.log('用户名或密码错误')
               }
           })
      }
  }
//在el-button 加上点击效果
<el-button type="primary" @click="handleLogin">登录</el-button>
6.登录错误处理

可以用element-ui直接弹出消息提示

this.$message.error("用户名或密码错误");
7.跳转

登录成功之后,需要马上跳转到个人首页

this.$router.push({
          name: "Home",
          params: {
          username: this.loginForm.username
       }
});
8.处理跨域,在config/index.js中的dev对象加上
proxyTable: {
      '/api/**': {
        target: 'http://127.0.0.1:6600/',
        changeOrigin: true, //改变源
        pathRewrite: {
          '^/api': '/'
        }
      },
    }

五.项目打包与后端整合

1.项目打包

打开config文件夹下index.js,修改build 对象的属性assetsPublicPath,改为

assetsPublicPath: './',

dev.env.js 和 prod.env.js 分别对应的是开发环境和生产环境的配置, 可以分别在这两个文件夹添加BASE_API 属性,表示请求服务器的前缀,而生产环境下直接设置为'"/"'就可以了,因为打包后是直接放在服务器下的,跟服务器相同域名

'use strict'
module.exports = {
  NODE_ENV: '"production"',
  BASE_API: '"/"'
}

在终端输入

npm run build

等运行完成如果出现

  Tip: built files are meant to be served over an HTTP server.
  Opening index.html over file:// won't work.

表示编译成功,在根目录的dist文件夹下,则是编译好的文件

2.集成到Spring boot

把dist下面的所有文件,包括static文件夹 和 index.html 直接扔到 spring boot项目中resources的static文件夹中
重启项目,就完成了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,649评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,039评论 25 707
  • 2016年10月14日19点10分,我读完了《源泉》。
    野鹤橙阅读 208评论 0 0
  • 如果生活欺骗了你?你该怎么办?是选择懦弱的躲在阴暗的的角落里,还是会选择勇敢面对? 有一句话说十分...
    娟LHM阅读 543评论 0 0
  • 在参加完李鑫老师的课程,利用书都不会读,你还想成功来练习王者速读跟眼脑直映并且导图的输出。 在读这本书之前,我先确...
    Tina_fa0e阅读 125评论 2 0