全局安装vue
yarn global add @vue/cli
# OR
npm install -g @vue/cli
安装后 vue -V 检查是否安装上了
创建项目-启动服务
创建项目
==注意: 项目名不能带大写字母, 中文和特殊符号==
# vue和create是命令, vuecli-demo是自己的文件夹名
vue create vuecli-demo
选择模板
==可以上下箭头选择, 回车确定, 弄错了ctrl+c从第1步来==
选择包管理器
终端切换脚手架项目下, 启动内置的==webpack热更新开发服务器==
cd vuecil-demo
yarn serve
# 或 npm run serve
脚手架-目录分析
vuecil-demo # 项目目录
├── node_modules # 项目依赖的第三方包
├── public # 静态文件目录
├── favicon.ico# 浏览器小图标
└── index.html # 单页面的html文件(网页浏览的是它)
├── src # 业务文件夹
├── assets # 静态资源
└── logo.png # vue的logo图片
├── components # 组件目录
└── HelloWorld.vue # 欢迎页面vue代码文件
├── App.vue # 整个应用的根组件
└── main.js # 入口js文件
├── .gitignore # git提交忽略配置
├── babel.config.js # babel配置
├── package.json # 依赖包列表
├── README.md # 项目说明
└── yarn.lock # 项目包版本锁定和缓存地址
主要文件及含义
node_modules – 第三方依赖包
public/index.html – 浏览器运行的网页
src/main.js – webpack打包的入口文件
src/App.vue – Vue入口页面
package.json – 依赖包列表文件和自定义命令
脚手架-代码和结构分析
脚手架-自定义配置
src并列处, 新建vue.config.js
/* 覆盖webpack的配置 */
module.exports = {
devServer: { // 自定义服务配置
open: true, // 自动打开浏览器
port: 3000
}
}
脚手架-eslint了解
例子: 先在main.js 随便声明个变量, 但是不要使用
运行后观察发现, 终端和页面都报错了
方式1: 手动解决掉错误, 以后项目中会讲如何自动解决
方式2: 暂时关闭eslint检查(因为现在主要精力在学习Vue语法上), 在vue.config.js中配置后重启服务
脚手架-单vue文件
-
template里只能有一个根标签
-
vue文件-独立模块-作用域互不影响
-
style配合scoped属性, 保证样式只针对当前template内标签生效
-
vue文件配合webpack, 把他们打包起来插入到index.html
<!-- template必须, 只能有一个根标签, 影响渲染到页面的标签结构 -->
<template>
<div>欢迎使用vue</div>
</template>
<!-- js相关 -->
<script>
export default {
name: 'App'
}
</script>
<!-- 当前组件的样式, 设置scoped, 可以保证样式只对当前页面有效 -->
<style scoped>
</style>
脚手架-清理欢迎界面
src/App.vue默认有很多内容, 可以全部删除留下框
<template>
<div></div>
</template>
<script>
export default {
}
</script>
<style>
</style>
assets 和 components 文件夹下的一切删除掉 (不要logo和HelloWorld页面)
Vue语法-插值表达式
语法: {{ 表达式 }}; 又叫声明式渲染/文本插值
<template>
<div>
<!-- 2. 变量的值, 显示到标签内 -->
<h1>{{ msg }}</h1>
<h2>{{ obj.username }}</h2>
<h3>{{ obj.age > 18 ? "成年了" : "未成年" }}</h3>
</div>
</template>
<script>
// 目标: 插值表达式
// 作用: vue变量, 放到标签里数据展示
export default {
// 1. data 函数内, 定义变量
data() {
return {
msg: "Hello, vue",
obj: {
username: "翟潇闻",
age: 22,
},
};
},
};
</script>
<style>
</style>
Vue基础-MVVM设计模式
安装插件,关闭浏览器再打开
v-bind动态属性 案例
<template>
<div>
<a v-bind:href="url">点我去逛淘宝</a>
<img :src="imgUrl" alt="" />
</div>
</template>
<script>
// 目标: v-bind动态属性
// 语法: v-bind:属性名="vue变量名"
// 作用: 标签的原生属性添加vue变量
export default {
data() {
return {
url: "http://www.taobao.com",
imgUrl:
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fnimg.ws.126.net%2F%3Furl%3Dhttp%253A%252F%252Fdingyue.ws.126.net%252F2021%252F0609%252F828edd35j00quecq9000mc000hs00hsc.jpg%26thumbnail%3D650x2147483647%26quality%3D80%26type%3Djpg&refer=http%3A%2F%2Fnimg.ws.126.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1634961582&t=a94eb063ee6525abe6f5eabd894caa04",
};
},
};
</script>
<style>
</style>
v-on事件绑定 案例
<template>
<div>
<!-- vue指令: v-on事件绑定 -->
<p>剩余数量: {{ count }}</p>
<button v-on:click="addFn">增加1个</button>
<button v-on:click="addCountFn(5)">点击增加5件</button>
<button @click="subFn">减少1个</button>
</div>
</template>
<script>
// 目标: 绑定事件
// 语法:
// v-on:事件名="methods里方法名"
// v-on:事件名="methods里方法名(值)"
export default {
data() {
return {
// 变量(属性)
count: 10,
};
},
methods: {
// 方法
addFn() {
// this: 当前vue文件(组件)对象, webpack在运行打包时, 会把data/methods里变量名和方法名, 统统添加到this身上
// console.log(this)
this.count++;
},
addCountFn(num) {
this.count += num;
},
subFn() {
this.count--;
},
},
};
</script>
<style>
</style>
案例-点击说笑话
<template>
<div>
<p>{{ word }}</p>
<button @click="btnFn">点击说笑话</button>
</div>
</template>
<script>
// 需求: 点击切换随机笑话
// 1. 准备标签和变量
// 2. 绑定点击事件
// 3. 点击产生随机数, 换出对应笑话, 设置给word变量影响页面
// 注意: jokeArr
export default {
data() {
return {
word: "这里是一条笑话",
jokeArr: [
"我去相亲网站去了, 那你找到对象了吗? 不! 我找到了他们网站的一个Bug",
"这个需求很简单, 怎么实现我不管, 明天上线",
"程序员是推动这个世界进步的人",
"某日,白素贞放了个屁,许仙恍然大悟道'娘子,莫非你是响尾蛇。'",
],
};
},
methods: {
btnFn() {
let num = Math.floor(Math.random() * this.jokeArr.length);
this.word = this.jokeArr[num];
},
},
};
</script>
<style>
</style>
v-on事件对象
<template>
<div>
<a href="" @click="one">无传参</a> <br />
<a href="" @click="two(10, $event)">有传参, 需要手动传递$event</a>
</div>
</template>
<script>
// 目标: 如何获取事件对象
// 语法:
// 事件触发 "无传参", 直接接收事件对象
// 事件触发 "传参", 显示写出$event(vue内置变量)-传递事件对象
export default {
methods: {
one(ev) {
ev.preventDefault();
},
two(num, ev) {
ev.preventDefault();
},
},
};
</script>
<style>
</style>
v-on修饰符
<template>
<div @click="fatherFn">
<button @click.stop="btnFn">.stop阻止事件冒泡</button>
<a href="http://www.baidu.com" @click.prevent="aFn">.prevent阻止默认行为</a>
</div>
</template>
<script>
// 目标: vue事件的修饰符
// 修饰符语法:
// @事件名.修饰符="methods里的方法名"
// stop - 阻止冒泡
// .prevent - 阻止默认行为
export default {
methods: {
fatherFn() {
console.log("father点击事件被触发");
},
btnFn() {
console.log("btn点击事件被触发了");
},
aFn() {
console.log("a标签的点击事件触发了");
},
},
};
</script>
<style>
</style>
v-on按键修饰符
<template>
<div>
<input type="text" @keyup.enter="enterFn" />
<br />
<input type="text" @keyup.esc="escFn" />
</div>
</template>
<script>
// 目标: 键盘事件 - 修饰符
// 语法:
// @事件名.修饰符="methods里方法名"
// .enter - 只有触发enter按键才会执行函数体
export default {
methods: {
enterFn() {
console.log("用户按了enter键");
},
escFn() {
console.log("用户按了esc键盘");
},
},
};
</script>
<style>
</style>
案例-翻转世界
<template>
<div>
<h1>{{ message }}</h1>
<button @click="btnFn">逆转世界</button>
</div>
</template>
<script>
// 目标: 点击翻转地球
// 1. 准备标签和变量初始值
// 2. 绑定点击事件
export default {
data() {
return {
message: "Hello, World",
};
},
methods: {
btnFn() {
// 3. 转成数组
let arr = this.message.split("");
// 4. 数组翻转元素
arr.reverse();
// 5. 数组转回字符串
this.message = arr.join("");
// this.message = this.message.split("").reverse().join("")
},
},
};
</script>
<style>
</style>
v-model双向绑定1
<template>
<div>
<div>
<span>用户名:</span>
<input type="text" v-model="username" />
</div>
<div>
<span>密码:</span>
<input type="password" v-model="userpass" />
</div>
</div>
</template>
<script>
// 目标: v-model指令作用
// 作用: 把value属性和Vue变量(双向)绑定在一起
// 语法: v-model="vue变量"
// 双向:
// 表单标签值(value属性)改变 -> vue变量
// vue变量改变 -> value属性改变(试图更新)
export default {
data() {
return {
username: "新月",
userpass: "",
};
},
};
</script>
<style>
</style>
v-model双向绑定2
<template>
<div>
<div>
<span>用户名:</span>
<input type="text" v-model="username" />
</div>
<div>
<span>密码:</span>
<input type="password" v-model="userpass" />
</div>
<div>
<span>来自于:</span>
<!-- select身上写v-model, option身上写value -->
<select v-model="from">
<option value="北京">北京市</option>
<option value="哈尔滨">哈尔滨市</option>
<option value="赤峰">赤峰市</option>
</select>
</div>
<div>
<span>爱好</span>
<!-- v-model遇到复选框,
vue变量 非数组类型 关联复选框 checkbox属性(true/false),
vue变量 数组类型 关联复选框 value属性值
-->
<input type="checkbox" v-model="hobby" value="抽烟" />抽烟
<input type="checkbox" v-model="hobby" value="喝酒" />喝酒
<input type="checkbox" v-model="hobby" value="唱歌" />唱歌
</div>
<div>
<span>单选</span>
<input type="radio" value="男" name="sex" v-model="gender" />男
<input type="radio" value="女" name="sex" v-model="gender" />女
</div>
<div>
<span>自我介绍</span>
<textarea v-model="intro"></textarea>
</div>
</div>
</template>
<script>
// 目标: v-model指令作用
// 作用: 把value属性和Vue变量(双向)绑定在一起
// 语法: v-model="vue变量"
// 双向:
// 表单标签值(value属性)改变 -> vue变量
// vue变量改变 -> value属性改变(试图更新)
export default {
data() {
return {
username: "翟潇闻",
userpass: "",
from: "赤峰",
hobby: [],
gender: "",
intro: "",
};
},
};
</script>
<style>
</style>
v-model修饰符
<template>
<div>
<div>
<span>年龄</span>
<input type="text" v-model.number.lazy="age" />
</div>
<div>
<span>人生格言</span>
<input type="text" v-model.trim="motto" />
</div>
</div>
</template>
<script>
// 目标: v-model 的修饰符
// 注意: 获取标签上的值, 都是字符串
// 修饰符
// .number - 用parseFloat => 数值 => vue变量
// .trim - 去除首尾空格 => vue变量
// .lazy - 等待change事件再同步数据 => vue变量
export default {
data() {
return {
age: 0,
motto: "",
};
},
};
</script>
<style>
</style>
v-html
<template>
<div>
<p v-html="str">{{ code }}</p>
</div>
</template>
<script>
// 目标: v-html指令
// 作用: 快速设置innerHTML属性值
// 语法: v-html="vue变量"
// 注意: v-html会覆盖插值表达式
export default {
data() {
return {
code: 100,
str: "<span>我是span标签</span>",
};
},
};
</script>
<style>
</style>
v-show和v-if
<template>
<div>
<div>
<h1 v-show="one">v-show控制标签显示/隐藏</h1>
<h1 v-if="two">v-if也能控制标签显示/隐藏</h1>
</div>
<div>
<p v-if="age > 18">我成年了</p>
<p v-else>未成年</p>
</div>
</div>
</template>
<script>
// 目标: v-show 和 v-if
// 作用: 控制标签显示/隐藏
// 语法:
// v-show="vue变量"
// v-if="vue变量"
// 注意: vue变量为true(显示),false(隐藏/销毁)
// v-show 隐藏 用display: none方式 - 频繁切换用这个
// v-if 隐藏 用从DOM树删除方式
// 目标: v-if 和 v-else
// 作用(场景): 2块标签, 要"互斥"显示
export default {
data() {
return {
one: true,
two: true,
age: 20,
};
},
};
</script>
<style>
</style>
案例-折叠面板
<template>
<div id="app">
<h3>案例:折叠面板</h3>
<div>
<div class="title">
<h4>芙蓉楼送辛渐</h4>
<span class="btn" @click="btnFn"> 收起 </span>
</div>
<!-- 4. 控制下面v-show显示 -->
<div class="container" v-show="isShow">
<p>寒雨连江夜入吴,</p>
<p>平明送客楚山孤。</p>
<p>洛阳亲友如相问,</p>
<p>一片冰心在玉壶。</p>
</div>
</div>
</div>
</template>
<script>
// 目标: 折叠面板
// 1. 准备标签+样式(less)
// 2. 下载less和less-loader(注意版本号)
// yarn add less@3.0.4 less-loader@5.0.0 -D
// 注意: webpack在使用时, 版本一定要对应,否则一个新的一个旧的(旧的里面没有另外一个新的包方法就报错)
// 脚手架用的是webpack4.0版本 -> less-loader8.0以下的版本
// 昨天webpack5.0 -> 最新的less-loader8和8以上的版本
export default {
data() {
return {
isShow: true,
};
},
methods: {
btnFn() {
// 5. 把变量值改变, 影响v-show的效果
if (this.isShow === true) {
this.isShow = false;
} else if (this.isShow === false) {
this.isShow = true;
}
},
},
};
</script>
<style lang="less">
// 这里的样式代码, 都是less语法的
// webpack内置了less的设置-(less-loader配置好了)
// 但是: webpack没给下载less和less-loader2个包
body {
background-color: #ccc;
#app {
width: 400px;
margin: 20px auto;
background-color: #fff;
border: 4px solid blueviolet;
border-radius: 1em;
box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.5);
padding: 1em 2em 2em;
h3 {
text-align: center;
}
.title {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid #ccc;
padding: 0 1em;
}
.title h4 {
line-height: 2;
margin: 0;
}
.container {
border: 1px solid #ccc;
padding: 0 1em;
}
.btn {
/* 鼠标改成手的形状 */
cursor: pointer;
}
}
}
</style>
v-for
<template>
<div>
<ul>
<li v-for="(item, index) in arr" :key="index">{{ item }}</li>
</ul>
<br />
<ul>
<li v-for="obj in stuArr" :key="obj.id">
<span>{{ obj.name }}</span>
<span>{{ obj.sex }}</span>
<span>{{ obj.hobby }}</span>
</li>
</ul>
</div>
</template>
<script>
// 目标: v-for用数据循环生成标签
// 语法: v-for="(值变量名, 索引变量名) in 目标结构"
// 效果: 每次遍历数组里每个值, 创建一次当前所在标签结构
// 每次遍历时, 都是独立执行的
// key作用: 提高更新的性能(给索引)
export default {
data() {
return {
arr: ["春天", "夏天", "秋天", "冬天"],
stuArr: [
{
id: 1001,
name: "孙悟空",
sex: "男",
hobby: "吃桃子",
},
{
id: 1002,
name: "猪八戒",
sex: "男",
hobby: "背媳妇",
},
],
};
},
};
</script>
<style>
</style>
作业1-逛水果店
<template>
<div>
<table width=800 style="text-align: center; margin: 0 auto;" border=1>
<caption>欢迎光临_vue开发的收银系统_水果店</caption>
<tr>
<th>苹果 {{ price }} ¥ / 斤, 折扣 < {{ dis * 10 }} 折 ></th>
</tr>
<tr>
<td>
请输入你要购买的斤数 <input type="number" v-model.number="count" placeholder="斤数">
</td>
</tr>
<tr>
<td>
<button @click="celFn">结账买单~</button>
</td>
</tr>
<tr>
<td>
结账单:
<span>总价: {{ allPrice }} ¥元</span>
<span>折后价: {{ disPrice }} ¥元</span>
<span>省了: {{ savePrice }} ¥元</span>
</td>
</tr>
</table>
</div>
</template>
<script>
export default {
data(){
return{
price: 10, // 单价
dis: 0.8, // 折扣
count: 0, // 斤数
allPrice: 0, // 总价
disPrice: 0, // 折扣价
savePrice: 0 // 省了多钱
}
},
methods: {
celFn(){
this.allPrice = this.price * this.count;
this.disPrice = this.allPrice * this.dis;
this.savePrice = this.allPrice - this.disPrice;
}
}
}
</script>
<style>
</style>
作业2-选择喜欢的
<template>
<div>
<p>请选择你喜欢的专栏</p>
<div class="one" v-for="(str, index) in titleArr" :key="index">
<input type="checkbox" :value="str" v-model="selArr" /><span>{{
str
}}</span>
</div>
<div>
<ul>
<li v-for="(str, index) in selArr" :key="index">{{ str }}</li>
</ul>
</div>
</div>
</template>
<script>
export default {
data() {
return {
titleArr: [
"科幻",
"喜剧",
"动漫",
"冒险",
"科技",
"军事",
"娱乐",
"奇闻",
], // 频道数组
selArr: [], // 用户选中的频道
};
},
};
</script>
<style>
one {
display: inline-block;
}
</style>
作业3-购物车
<template>
<div id="app">
<table class="tb">
<tr>
<th>编号</th>
<th>品牌名称</th>
<th>创立时间</th>
<th>操作</th>
</tr>
<!-- 循环渲染的元素tr -->
<!-- 语法:
v-for="(值变量, 索引变量) in 目标结构"
v-for="值变量 in 目标结构"
-->
<tr v-for="(item, index) in list" :key="item.id">
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.time }}</td>
<td>
<!-- delete删除 -->
<button @click="del(index)">删除</button>
</td>
</tr>
<!-- v-if="vue变量" -->
<!-- v-if 直接从DOM树上移除 -->
<tr v-if="list.length === 0">
<td colspan="4">没有数据咯~</td>
</tr>
</table>
</div>
</template>
<script>
export default {
data() {
return {
list: [
{ id: 1, name: "奔驰", time: "2020-08-01" },
{ id: 2, name: "宝马", time: "2020-08-02" },
{ id: 3, name: "奥迪", time: "2020-08-03" },
],
};
},
methods: {
del(index) {
// 删除按钮 - 得到索引, 删除数组里面的元素元素
this.list.splice(index, 1);
},
},
};
</script>
<style>
#app {
width: 600px;
margin: 10px auto;
}
.tb {
border-collapse: collapse;
width: 100%;
}
.tb th {
background-color: #0094ff;
color: white;
}
.tb td,
.tb th {
padding: 5px;
border: 1px solid black;
text-align: center;
}
.add {
padding: 5px;
border: 1px solid black;
margin-bottom: 10px;
}
</style>
作业4-点名系统
<template>
<div>
<h2>随机点名</h2>
<div class="box">
<span>名字是:</span>
<div class="qs">
<p>{{ uname }}</p>
</div>
</div>
<div class="btns">
<button class="start" @click="begin()">开始</button>
<button class="end" @click="over()">结束</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
names: [
"李林泽",
"刘东波",
"程小燕",
"冯阔",
"卢倩倩",
"公新月",
"郅建文",
],
uname: "这里显示名字",
timer: "",
cname: "",
};
},
methods: {
begin() {
clearInterval(this.timer);
this.timer = setInterval(this.valChange, 100);
},
valChange() {
let ind = Math.floor(Math.random() * this.names.length);
this.uname = this.names[ind];
this.cname = this.names[ind];
},
over() {
clearInterval(this.timer);
this.names.forEach((item, index) => {
if (item == this.cname) {
this.names.splice(index, 1);
}
});
if (this.names.length == 0) {
this.uname = "库里无人";
}
},
},
};
</script>
<style>
* {
margin: 0;
padding: 0;
}
h2 {
text-align: center;
}
.box {
width: 600px;
margin: 50px auto;
display: flex;
font-size: 25px;
line-height: 40px;
}
.qs {
width: 450px;
height: 40px;
color: red;
}
.btns {
text-align: center;
}
.btns button {
width: 120px;
height: 35px;
margin: 0 50px;
}
</style>
作业5-协议倒计时
<template>
<div>
<textarea name="" id="" cols="30" rows="10">
用户注册协议
欢迎注册成为京东用户!在您注册过程中,您需要完成我们的注册流程并通过点击同意的形式在线签署以下协议,请您务必仔细阅读、充分理解协议中的条款内容后再点击同意(尤其是以粗体或下划线标识的条款,因为这些条款可能会明确您应履行的义务或对您的权利有所限制)。
【请您注意】如果您不同意以下协议全部或任何条款约定,请您停止注册。您停止注册后将仅可以浏览我们的商品信息但无法享受我们的产品或服务。如您按照注册流程提示填写信息,阅读并点击同意上述协议且完成全部注册流程后,即表示您已充分阅读、理解并接受协议的全部内容,并表明您同意我们可以依据协议内容来处理您的个人信息,并同意我们将您的订单信息共享给为完成此订单所必须的第三方合作方(详情查看
</textarea>
<br />
<!-- 属性不给值, 默认就是true值 -->
<button class="btn" :disabled="off">
我已经阅读用户协议<span v-show="isShow">({{ value }})</span>
</button>
</div>
</template>
<script>
export default {
data() {
return {
timer: "",
value: 60,
off: true,
isShow: true,
};
},
methods: {
get() {
this.value--;
if (this.value === 0) {
this.off = false;
this.isShow = false;
}
},
},
mounted() {
this.timer = setInterval(this.get, 1000);
},
};
</script>
作业6-加加减减
<template>
<div id="app">
<ul>
<li v-for="(item, ind) in arr" :key="item">
<span>{{ item }}</span>
<button @click="del(ind)">删除</button>
</li>
</ul>
<button @click="add">生成</button>
</div>
</template>
<script>
export default {
data() {
return {
arr: [5, 7, 1],
};
},
methods: {
add() {
this.arr.push(Math.floor(Math.random() * 20));
},
del(index) {
this.arr.splice(index, 1);
},
},
};
</script>
<style>
</style>
作业7-帅哥美女走一走
<template>
<div id="app">
<ul>
<li v-for="item in myArr" :key="item">{{ item }}</li>
</ul>
<button @click="btnFn">走一走</button>
</div>
</template>
<script>
export default {
data() {
return {
myArr: ["帅哥", "美女", "程序猿"],
};
},
methods: {
btnFn(){
// 头部的数据加入到末尾,在删除头部的数据
this.myArr.push(this.myArr[0])
this.myArr.shift()
}
}
}
</script>
笔记