Vue核心技术与实战--2023.7.4

Vue核心技术与实战–2023.7.4

1.vue和JQuery的区别?

vue:只操作数据,视图更新是异步的(所有的数据都更新完毕,再更新视图)
JQuery:从dom树中选择操作dom对象, $函数选择节点操作dom

2.MV VM

数据驱动视图=模型驱动视图
数据=模型
VM视图和模型的双向数据绑定,v-model来实现双向绑定

h5面经

iPhoneSE 375*667
Vue核心技术与实战--2023.7.4_第1张图片
Vue核心技术与实战--2023.7.4_第2张图片

基于VueCli自定义创建项目架子

Vue核心技术与实战--2023.7.4_第3张图片
步骤:

  1. 创建面经项目vue create hm-mj-h5

  2. Vue核心技术与实战--2023.7.4_第4张图片

  3. 配置
    Vue核心技术与实战--2023.7.4_第5张图片

  4. 确认配置选择
    Vue核心技术与实战--2023.7.4_第6张图片

  5. Vue核心技术与实战--2023.7.4_第7张图片

  6. 打开项目–根目录打开 不要嵌套

  • 用VScode打开 cd .
    在这里插入图片描述
  • 直接拖
ESlint 代码规范

JavaScript Standard Style 规范说明 https://standardjs.com/rules-zhcn. html
Vue核心技术与实战--2023.7.4_第8张图片
npm run lint 命令自动改成规范代码 (删除代码需要手动解决)

  • 代码规范
    Vue核心技术与实战--2023.7.4_第9张图片
    ==包含隐式转换
代码规范错误

Vue核心技术与实战--2023.7.4_第10张图片

Vue核心技术与实战--2023.7.4_第11张图片
配置项
Vue核心技术与实战--2023.7.4_第12张图片

 // 当保存的时候,eslint自动帮我们修复错误
    "editor.codeActionsOnSave": {
        "source.fixAll": true
    },
    // 保存代码,不自动格式化
    "editor.formatOnSave": false

调整初始化目录

Vue核心技术与实战--2023.7.4_第13张图片

  • 删除asets中的logo
  • 删除index.js中的配置路由
  • 删除app.vue中template中的内容
  • 修改APP.vue和路由配置
  • 增加api和utils两个文件夹
提交git仓库

Vue核心技术与实战--2023.7.4_第14张图片

  • 提交git仓库
  • 直接提交----> 内容写 初始化文件 ----> 提交

vant移动端组件库

vant2-----vue2

  1. 安装 npm i vant@latest-v2 -S
    Vue核心技术与实战--2023.7.4_第15张图片
  • 错误
    • ①强行解决
      Vue核心技术与实战--2023.7.4_第16张图片
    • ②解决版本依赖 (版本降级命令) npm i -D @vue/[email protected]
      成功后将6.1.0降级为5.1.0
      Vue核心技术与实战--2023.7.4_第17张图片
      Vue核心技术与实战--2023.7.4_第18张图片
  1. 引入组件
  • 方式3导入所有组件—main.js
//main.js
import Vue from 'vue';
import Vant from 'vant';
import 'vant/lib/index.css';

Vue.use(Vant);
  1. 使用–放到组件库中(app.vue)
//app.vue
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>

Vue核心技术与实战--2023.7.4_第19张图片

vant 全部导入 和 按需导入
  • 全部导入

Vue核心技术与实战--2023.7.4_第20张图片

  • 按需导入
  1. 解决版本依赖 (版本降级命令) npm i -D @vue/[email protected]
    成功后将6.1.0降级为5.1.0
    Vue核心技术与实战--2023.7.4_第21张图片
    Vue核心技术与实战--2023.7.4_第22张图片

  2. 需要先安装插件 npm i babel-plugin-import -D

# 安装插件
npm i babel-plugin-import -D
  1. babel.config.js中增加以下代码
//babel.config.js
 "plugins": [
    ["import", {
      "libraryName": "vant",
      "libraryDirectory": "es",
      "style": true
    }]
  ]
  1. 在main.js中引入
import '@/utils/vant-utils.js'
  1. app.vue
<van-field  label="文本" placeholder="请输入用户名" />

<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="default">默认按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
  
  1. vant-utils.js
// 按需引入
import Vue from 'vue'
import { Field, Button } from 'vant'

Vue.use(Field)
Vue.use(Button)

按需导入成功
Vue核心技术与实战--2023.7.4_第23张图片

Vue核心技术与实战--2023.7.4_第24张图片

其他组件库

Vue核心技术与实战--2023.7.4_第25张图片

移动端适配—不同屏幕等比缩放

vw适配----Viewport 布局(Vant2–进阶用法)

  1. 安装插件(2种)
  • npm install postcss-px-to-viewport --save-dev
  • 或者使用命令下载插件 npm i postcss-px-to-viewport
  1. 在根目录创建postcss.config.js文件
// postcss.config.js
module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      viewportWidth: 375,//视口宽度是375像素
    },
  },
};
  1. 重新启动 npm run serve
    页面等比缩放—>移动端适配成功

提交Git— 移动端适配

Vue核心技术与实战--2023.7.4_第26张图片

提交Git— 四个一级路由注册

  1. 在views引入组件(4个一级路由页面)
  2. 在index.js中配置路由表
    Vue核心技术与实战--2023.7.4_第27张图片

一级路由注册

Vue核心技术与实战--2023.7.4_第28张图片

路由设计配置

目标:阅读vant组件库文档,实现底部导航 tabbar
Vue核心技术与实战--2023.7.4_第29张图片

  1. 在vant-utils.js引入
//vant-utils.js
import Vue from 'vue';
import { Tabbar, TabbarItem } from 'vant';

Vue.use(Tabbar);
Vue.use(TabbarItem);
  1. 在layout.vue中使用
// layout.vue
<template>
  <div class="layout-page">
    <div>首页架子 - 内容区域</div>

    <div>

      <van-tabbar v-model="active">
        <van-tabbar-item icon="notes-o">面经</van-tabbar-item>
        <van-tabbar-item icon="star-o">收藏</van-tabbar-item>
        <van-tabbar-item icon="like-o">喜欢</van-tabbar-item>
        <van-tabbar-item icon="user-o">我的</van-tabbar-item>
      </van-tabbar>
    </div>
  </div>
</template>

<script>
export default {
  name: 'LayoutPage',
  data () {
    return {
      active: 0
    }
  }
}
</script>

<style lang="less" scoped></style>
  1. 修改文字、图标,进行定制
 <van-tabbar v-model="active">
        <van-tabbar-item icon="notes-o">面经</van-tabbar-item>
        <van-tabbar-item icon="star-o">收藏</van-tabbar-item>
        <van-tabbar-item icon="like-o">喜欢</van-tabbar-item>
        <van-tabbar-item icon="user-o">我的</van-tabbar-item>
      </van-tabbar>
    </div>

Vue核心技术与实战--2023.7.4_第30张图片

主题定制—修改less变量的值

定制方法
1.步骤一 引入样式源文件

  • 按需引入样式(推荐)
    在 babel.config.js 中配置按需引入样式源文件
  // 指定样式路径
        style: (name) => `${name}/style/less`,
  1. 步骤二 修改样式变量
  • 如果 vue-cli 搭建的项目,可以在 vue.config.js 中进行配置。
 css: {
    loaderOptions: {
      less: {
        // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
        lessOptions: {
          modifyVars: {
            // 直接覆盖变量
            'text-color': '#111',
            'border-color': '#eee',
            // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
            hack: `true; @import "your-less-file-path.less";`,
          },
        },
      },
    },
  },
  1. 修改颜色
  • 找到icon对应的控制的颜色#1989fa
    Vue核心技术与实战--2023.7.4_第31张图片
  • 在文档中找到控制#1989fa的名字@blue: #1989fa;
    Vue核心技术与实战--2023.7.4_第32张图片
  • 改为需要的颜色 blue: ‘#FA6D1D’
    Vue核心技术与实战--2023.7.4_第33张图片
  • 修改成功
    Vue核心技术与实战--2023.7.4_第34张图片

提交Git— 完成了vant的主题定制

Vue核心技术与实战--2023.7.4_第35张图片

2023.7.4面试补充–数据劫持 Object.defineProperty()

Object.defineProperty() 是 JavaScript 中用于定义或修改对象属性的方法。它允许你精确地定义对象属性的特性,包括可写性(writable)、可枚举性(enumerable)、可配置性(configurable)以及设置访问器属性(getter 和 setter)。

语法:Object.defineProperty(obj, prop, descriptor)
参数说明:
obj:要定义属性的对象。
prop:要定义或修改的属性名。
descriptor:一个包含属性的特性的对象。
descriptor 对象包含以下可选的属性:

value:设置属性的值(仅用于数据属性,而非访问器属性)。
writable:指定属性是否可写入(true/false)。
enumerable:指定属性是否可枚举(true/false)。
configurable:指定属性是否可配置(true/false)。
get:定义属性的 getter 函数(计算属性)。
set:定义属性的 setter 函数(计算属性)。
Vue核心技术与实战--2023.7.4_第36张图片

Vue核心技术与实战--2023.7.4_第37张图片
Vue核心技术与实战--2023.7.4_第38张图片

M==>V V==>M 简单模型 v-model 底层的原理 :value + @input

Vue核心技术与实战--2023.7.4_第39张图片
v-model 底层的原理 :value + @input
留一个问题: username 和 pssword 数据发生变化,都会导致 input 和 pNode 更新视图

2023.7.5

响应式布局和移动端适配的区别

响应式布局和移动端适配是为了在移动设备上提供更好的用户体验而采取的两种不同的方法。

**响应式布局是一种设计方法,它使网页可以自动适应不同的屏幕尺寸和设备类型,以提供一致的布局和体验。**在响应式布局中,使用媒体查询、弹性网格布局和其他技术来调整网页的布局、字体大小、图像大小和功能,以适应不同设备上的屏幕大小。

**移动端适配是指根据具体的移动设备类型和屏幕尺寸来调整和优化网页的布局和功能,以更好地适应移动设备的特点。**移动端适配可以通过多种技术实现,例如使用CSS媒体查询、JavaScript框架(如Bootstrap、Foundation等)或专门为移动设备设计的CSS框架(如Ionic、Ant Design Mobile等)。移动端适配可以根据具体的移动设备类型和屏幕大小提供不同的布局、字体大小、交互方式等。

区别:

  1. 设计理念:响应式布局是为了在不同设备上提供一致的体验,通过调整布局和功能来适应不同屏幕尺寸。移动端适配更关注于移动设备上的特定布局和功能,以提供更好的移动体验。

  2. 实现方式:响应式布局使用CSS媒体查询和弹性网格布局等技术,通过一个网页来适应不同设备。移动端适配可以使用不同的方式来适应移动设备,如媒体查询、JavaScript框架或移动专用的CSS框架。

  3. 维护和开发成本:响应式布局只需要维护一个网页,适应不同设备,因此维护和开发成本相对较低。移动端适配可能需要维护多个布局和功能,因此可能会涉及更高的维护和开发成本。

综上所述,响应式布局和移动端适配是两种不同的方法,用于在移动设备上提供更好的用户体验。选择哪种方法取决于项目需求、设备范围和开发资源。有些情况可能需要同时使用响应式布局和移动端适配来实现最佳的移动体验。

基于底部导航,完成二级路由配置

  1. 写二级路由表
//index.js
import Article from '@/views/Article'
import Collect from '@/views/Collect'
import Like from '@/views/Like'
import User from '@/views/User'

const router = new VueRouter({
  // 路由表
  routes: [
    {
      path: '/login',
      component: Login
    },
    {
      path: '/detail',
      component: Detail
    },
    {
      path: '/register',
      component: Register
    },
    {
      path: '/',
      component: Layout,
      // 用户访问/跟路由的时候,会自动重定向到/article页面
      redirect: '/article', // 重定向
      children: [
        {
          path: '/article',
          component: Article
        },
        {
          path: '/collect',
          component: Collect
        },
        {
          path: '/like',
          component: Like
        },
        {
          path: '/user',
          component: User
        }
      ]
    }
  ]
})
  1. 写二级路由出口
//Layout.vue
<router-view></router-view>

提交Git— 完成二级路由的定义

Vue核心技术与实战--2023.7.4_第40张图片

完成tabbar的导航功能

replace 是否在跳转时替换当前页面历史(是否能回退)

//Layout.vue
  <van-tabbar v-model="active" route>
        <van-tabbar-item replace to="/article"  icon="notes-o">面经</van-tabbar-item>
        <van-tabbar-item replace to="/collect"  icon="star-o">收藏</van-tabbar-item>
        <van-tabbar-item replace to="/like"  icon="like-o">喜欢</van-tabbar-item>
        <van-tabbar-item replace to="/user"  icon="user-o">我的</van-tabbar-item>
      </van-tabbar>

提交Git—完成tabbar的导航功能

Vue核心技术与实战--2023.7.4_第41张图片

完成登录页面的静态结构搭建

NavBar 导航栏

//vant-utils.js
import Vue from 'vue';
import { NavBar } from 'vant';

Vue.use(NavBar);
 <van-nav-bar title="面经登录"/>

Form 表单

//vant-utils.js
import Vue from 'vue';
import { Form } from 'vant';
import { Field } from 'vant';

Vue.use(Form);
Vue.use(Field);

round 提交的圆角

//Login.vue
<van-form @submit="onSubmit">
  <van-field
    v-model="username"
    name="用户名"
    label="用户名"
    placeholder="用户名"
    :rules="[{ required: true, message: '请填写用户名' }]"
  />
  <van-field
    v-model="password"
    type="password"
    name="密码"
    label="密码"
    placeholder="密码"
    :rules="[{ required: true, message: '请填写密码' }]"
  />
  <div style="margin: 16px;">
  //round 提交的圆角
    <van-button round block type="info" native-type="submit">提交</van-button>
  </div>
</van-form>
export default {
  data() {
    return {
      username: '',
      password: '',
    };
  },
  methods: {
    onSubmit(values) {
      console.log('submit', values);
    },
  },
};

提交Git—完成登录页面的静态结构搭建

Vue核心技术与实战--2023.7.4_第42张图片

登录页面的规则校验

trigger 本项规则的触发时机,可选值为 onChange、onBlur
Vue核心技术与实战--2023.7.4_第43张图片
Vue核心技术与实战--2023.7.4_第44张图片
Vue核心技术与实战--2023.7.4_第45张图片

<template>
  <div class="login-page">
    登录页
    <van-nav-bar title="面经登录" />

    <van-form @submit="onSubmit">
      <van-field
        v-model="username"
        name="username"
        label="用户名"
        placeholder="用户名"
        :rules="usernameRules"
      />
      <van-field
        v-model="password"
        type="password"
        name="password"
        label="密码"
        placeholder="密码"
        :rules="passwordRules"
      />
      <div style="margin: 16px">
        <van-button round block type="info" native-type="submit"
          >提交</van-button
        >
      </div>
    </van-form>
  </div>
</template>

<script>
export default {
  data () {
    return {
      username: '',
      password: '',
      usernameRules: [
        { required: true, message: '请填写用户名' },
        { pattern: /^\w{6,20}$/, message: '用户名长度必须在6~20位之间' }
      ],
      passwordRules: [
        { required: true, message: '请填写密码' },
        { pattern: /^[0-9]{6,}$/, message: '密码必须是数字,且最短为6位' }
      ]
    }
  },
  methods: {
    onSubmit (values) {
      // console.log('submit', values)

    }
  }

}

</script>
正则表达式 只能是数字最少六位

你可以使用以下正则表达式来匹配只包含数字且至少六位的字符串:

/^\d{6,}$/

解释一下这个正则表达式:

  • ^ 表示匹配字符串的开头
  • \d 表示匹配任意数字字符(0-9)
  • {6,} 表示前面的表达式必须出现至少6次
  • $ 表示匹配字符串的结尾

因此,这个正则表达式能够确保字符串只包含数字,并且至少有六个数字字符。

提交Git—登录页面的规则校验

Vue核心技术与实战--2023.7.4_第46张图片

完成静态页面的注册页面及规则校验

提交Git—完成静态页面的注册页面及规则校验

Vue核心技术与实战--2023.7.4_第47张图片

完成登录请求测试代码

try{ } catch{ }不能捕获异步的异常

用于捕获同步异常,也就是说不能直接捕获promise异常
用async和await转为同步代码,就可以用try{}catch{}捕获错误
Vue核心技术与实战--2023.7.4_第48张图片

跨域问题

在这里插入图片描述

axios发起请求
  1. 安装依赖 命令npm i -S axios
  2. 点击提交之后发送登录请求(接口文档post请求 axios.post)
axios.post('http://interview-api-t.itheima.net/h5/user/login', {
          // values
          username: this.username,
          password: this.password
        })
  1. 登录参数是username:this.username,possword:this.possword
  2. 在组件中使用轻提示this.$toast(err.response.data.message)
//vant-utils.js
import { Toast } from 'vant';
Vue.use(Toast);
//Login.vue
Toast('提示内容');
  1. 捕获异常 .catch或者 try{ } catch{ }(不能捕获异步的异常),用async和await转为同步代码,就可以用try{}catch{}捕获错误
 methods: {
    async onSubmit (values) {
      console.log('submit', values)
      try {
        await axios.post('http://interview-api-t.itheima.net/h5/user/login', {
          // values
          username: this.username,
          password: this.password
        })
      } catch (err) {
        // conslole.log(err)
        this.$toast(err.response.data.message)
      }
    }
  }
<template>
  <div class="login-page">
    登录页
    <van-nav-bar title="面经登录" />

    <van-form @submit="onSubmit">
      <van-field
        v-model="username"
        name="username"
        label="用户名"
        placeholder="用户名"
        :rules="usernameRules"
      />
      <van-field
        v-model="password"
        type="password"
        name="password"
        label="密码"
        placeholder="密码"
        :rules="passwordRules"
      />
      <div style="margin: 16px">
        <van-button round block type="info" native-type="submit"
          >提交</van-button
        >
      </div>
    </van-form>
  </div>
</template>

<script>
import axios from 'axios'
export default {
  data () {
    return {
      username: '',
      password: '',
      usernameRules: [
        { required: true, message: '请填写用户名' },
        { pattern: /^\w{6,20}$/, message: '用户名长度必须在6~20位之间' }
      ],
      passwordRules: [
        { required: true, message: '请填写密码' },
        { pattern: /^[0-9]{6,}$/, message: '密码必须是数字,且最短为6位' }
      ]
    }
  },
  methods: {
    async onSubmit (values) {
      console.log('submit', values)
      try {
        await axios.post('http://interview-api-t.itheima.net/h5/user/login', {
          // values
          username: this.username,
          password: this.password
        })
      } catch (err) {
        // conslole.log(err)
        this.$toast(err.response.data.message)
      }
    }
  }

}

</script>

<style lang="less" scoped>
.link {
  color: #069;
  font-size: 12px;
  padding-right: 20px;
  float: right;
}
</style>

提交Git—完成了登录请求测试代码

Vue核心技术与实战--2023.7.4_第49张图片

完成注册请求测试代码

用户接口模块
面经接口模块
只关注请求地址和请求类型

//Register.vue
<template>
  <div class="register-page">
    <van-nav-bar title="面经注册" />
    <van-form @submit="onSubmit">
      <van-field
        v-model="username"
        name="username"
        label="用户名"
        placeholder="用户名"
        :rules="usernameRules"
      />
      <van-field
        v-model="password"
        type="password"
        name="password"
        label="密码"
        placeholder="密码"
        :rules="passwordRules"
      />
      <div style="margin: 16px">
        <van-button round block type="info" native-type="submit"
          >提交</van-button
        >
      </div>
    </van-form>
  </div>
</template>

<script>
//Register.vue
import axios from 'axios'
export default {
  name: 'RegisterPage',
  data () {
    return {
      username: '',
      password: '',
      usernameRules: [
        { required: true, message: '请填写用户名' },
        { pattern: /^\w{6,20}$/, message: '用户名长度必须在6~20位之间' }
      ],
      passwordRules: [
        { required: true, message: '请填写密码' },
        { pattern: /^[0-9]{6,}$/, message: '密码必须是数字,且最短为6位' }
      ]
    }
  },
  methods: {
    async onSubmit (values) {
      console.log('submit', values)
      try {
        await axios.post('http://interview-api-t.itheima.net/h5/user/login', {
          // values
          username: this.username,
          password: this.password
        })
      } catch (err) {
        // conslole.log(err)
        this.$toast(err.response.data.message)
      }
    }
  }
}
</script>

undefined和null不能再点 , 否则会报错 =>解决: 可选链操作符

Vue核心技术与实战--2023.7.4_第50张图片
可选链操作符

Vue核心技术与实战--2023.7.4_第51张图片

提交Git—完成注册和登录页面的测试请求

Vue核心技术与实战--2023.7.4_第52张图片

二层封装==>完成api请求的简单封装

  1. 封装一个方法放在user.js中方便调用
//user.js
import axios from 'axios'
import { Toast } from 'vant'
// 用户登录
export function userLogin (values) {
  return axios.post('http://interview-api-t.itheima.net/h5/user/login', values).catch(err => {
    console.log(err)
    Toast(err.response.data.message)
  })
}

// 用户注册
export async function userRegister (values) {
  return await axios.post('http://interview-api-t.itheima.net/h5/user/register', values).catch(err => {
    Toast(err.response.data.message)
  })
}

  1. 在Login.vue中引入
//Login.vue
import { userLogin } from '@/api/user'
methods: {
    async onSubmit (values) {
      await userLogin(values)
    }
  }
  1. 在Register.vue中引入
//Register.vue
import { userRegister } from '@/api/user'
methods: {
    async onSubmit (values) {
      console.log('submit', values)
      const res = await userRegister(values)
      if (res && res.data?.code === 10000) {
        this.$router.push('/Login')
      }
    }
  }

提交Git—完成api请求的简单封装

Vue核心技术与实战--2023.7.4_第53张图片

完成了axios请求模块的三层封装

请求模块

Vue核心技术与实战--2023.7.4_第54张图片
请求拦截器: 在请求真正发出去之前 , 对数据做最后的加工

响应拦截器: 在响应回来之后,把响应给应用数据之前做最后的加工

基地址(基础地址)
axios.create 方法会创建一个新的请求实例
后续会使用 instance 对象去发送请求

axios文档
Vue核心技术与实战--2023.7.4_第55张图片

封装到一个新的文件里面 utils/request.js

//request.js
import axios from 'axios'
import { Toast } from 'vant'

//  axios.create方法会创建一个新的请求实例
// 后续会使用instance 对象去发送请求
const instance = axios.create({
  //  基地址:会在后续的请求中,自动拼接到url上
  baseURL: 'http://interview-api-t.itheima.net'
})

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  });

在api/user.js文件下二级封装中修改

//user.js
// import axios from 'axios'
// import { Toast } from 'vant'
// 因为 @/utils/request 是默认导出,所以导入的时候,可以随便重新命名
import request from '@/utils/request'
// 用户登录
export function userLogin (values) {
  return request.post('/h5/user/login', values)
//   return axios.post('http://interview-api-t.itheima.net/h5/user/login', values).catch(err => {
//     console.log(err)
//     Toast(err.response.data.message)
//   })
}

// 用户注册
export async function userRegister (values) {
  return request.post('/h5/user/register', values)
//   return await axios.post('http://interview-api-t.itheima.net/h5/user/register', values).catch(err => {
//     Toast(err.response.data.message)
//   })
}

提交Git—完成了axios请求模块的三层封装

Vue核心技术与实战--2023.7.4_第56张图片

作业

Vue核心技术与实战--2023.7.4_第57张图片

//Register.vue
  methods: {
    async onSubmit (values) {
      console.log('submit', values)
      const res = await userRegister(values)
      if (res && res.data?.code === 10000) {
        localStorage.setItem('username', JSON.stringify(res.data.data.token))
        this.$router.push('/Login')
      }
       }
  }
}
//Login.vue
 methods: {
    async onSubmit (values) {
      const res = await userLogin(values)
      localStorage.setItem('username', JSON.stringify(res.data.data.token))
    }
  }

Vue核心技术与实战--2023.7.4_第58张图片

保存token到本地

Vue核心技术与实战--2023.7.4_第59张图片

setTimeout执行时候是同步的,执行结束后的结果是异步的

2023.7.6

  1. storage.js封装localstorage,拿到token
// utils/storage.js
// 专门提供对本地存储的操作
const KEY = 'hm-token'
// 写数据==>写token
export function setToken (token) {
  return localStorage.setItem(KEY, token)
}
// 读数据==>读token
export function getToken () {
  return localStorage.getItem(KEY)
}
// 删数据==>删token
export function delToken () {
  return localStorage.removeItem(KEY)
}

页面拦截访问

  1. 拦截或放行的关键点?用户是否有登录权证 token
  2. 路由守卫 分为 前置守卫 和 后置守卫
前置守卫beforeEach:在每个路由导航之前执行的全局前置守卫。可以用于进行登录验证、权限检查等操作。
  1. 路由前置守卫逻辑
    Vue核心技术与实战--2023.7.4_第60张图片
  • 模版
router.beforeEach((to, from, next) => {
  // 1.to往哪里去,到哪去的路由信息对象
  // 2.from 从哪里来,从哪来的路由信息对象
  // 3.next() 是否放行
  //        如果next()调用,就是放行
  //        next(路径) 拦截到某个路径页面
})
  1. 简单逻辑
//index.js
router.beforeEach((to, from, next) => {
  const token = getToken()
  if (token) {
    // 已登录
    next()
  } else {
    // 是否是去登录或注册页面
    if (to.path === '/login' || to.path === '/register') {
      next()
    } else {
      // 未登录
      next('/login')
    }
  }
})
  1. 写入一个白名单
//index.js
// 路由守卫
// 前置守卫 beforeEach
// to:去哪里
// from:从哪来
// next: 用来控制是否放行
// from: /login ==> to: /register
// from: /login ==> /user
// 定一个一个白名单列表
// 白名单:不需要登录就能访问的页面
import { getToken } from '@/utils/storage'

const whiteList = ['/login', '/register']
router.beforeEach((to, from, next) => {
  const token = getToken()
  // console.log(from, to)
  // 不放行:让路由跳转到另一个页面next('/user')
  if (token) {
    // 已登录
    next()
  } else {
    // 是否是去登录或注册页面
    // if (whiteList.index0f(to.path) >= 0) {
    if (whiteList.includes(to.path)) {
    // if (to.path === '/login' || to.path === '/register') {
      next()
    } else {
      // 未登录
      next('/login')
    }
  }

  // ②第二步 合并if 增加去掉else
  // if (token) {
  //   next()
  //   return
  // }
  // if (whiteList.includes(to.path)) {
  //   next()
  // }

  // ③第三步 转为卫语句
  // if (token || whiteList.includes(to.path)) {
  //   return next()
  // }
  // next('/login')
})
  1. 优化 ==> 将嵌套逻辑转为扁平逻辑(合并if 增加去掉else) => 卫语句

Vue核心技术与实战--2023.7.4_第61张图片

  • 卫语句
//index.js
const whiteList = ['/login', '/register']
router.beforeEach((to, from, next) => {
  const token = getToken()
  if (token || whiteList.includes(to.path)) {
    return next()
  }
  next('/login')
  })
路由守卫

在进入到某个路由页面之前,拦截一下,并根据特定条件决定是否允许继续导航

Vue核心技术与实战--2023.7.4_第62张图片

提交git—完成页面访问拦截的基本逻辑

Vue核心技术与实战--2023.7.4_第63张图片

后置守卫 afterEach:在每个路由导航之后执行的全局后置守卫。可以用于处理页面跳转后的清理工作、埋点统计等操作。

面经列表

Cell组件,备首页面经的基本布局D:\资料U盘\2023.6.23Vue\VueStudent\day08\02-笔记
//vant-utils.js
import { Cell } from 'vant'
Vue.use(Cell)
//Article.vue
<template>
  <div class="article-page">
    <nav class="my-nav van-hairline--bottom">
      <a
        href="javascript:;"
        >推荐</a
      >
      <a
        href="javascript:;"
        >最新</a
      >
      <div class="logo"><img src="@/assets/logo.png" alt=""></div>
    </nav>

    <van-cell class="article-item" >
      <template #title>
        <div class="head">
          <img src="http://teachoss.itheima.net/heimaQuestionMiniapp/%E5%AE%98%E6%96%B9%E9%BB%98%E8%AE%A4%E5%A4%B4%E5%83%8F%402x.png" alt="" />
          <div class="con">
            <p class="title van-ellipsis">宇宙头条校招前端面经</p>
            <p class="other">不风流怎样倜傥 | 2022-01-20 00-00-00</p>
          </div>
        </div>
      </template>
      <template #label>
        <div class="body van-multi-ellipsis--l2">
          笔者读大三, 前端小白一枚, 正在准备春招, 人生第一次面试, 投了头条前端, 总共经历了四轮技术面试和一轮hr面, 不多说, 直接上题&nbsp;一面
        </div>
        <div class="foot">点赞 46 | 浏览 332</div>
      </template>
    </van-cell>
    <van-cell class="article-item" >
      <template #title>
        <div class="head">
          <img src="http://teachoss.itheima.net/heimaQuestionMiniapp/%E5%AE%98%E6%96%B9%E9%BB%98%E8%AE%A4%E5%A4%B4%E5%83%8F%402x.png" alt="" />
          <div class="con">
            <p class="title van-ellipsis">宇宙头条校招前端面经</p>
            <p class="other">不风流怎样倜傥 | 2022-01-20 00-00-00</p>
          </div>
        </div>
      </template>
      <template #label>
        <div class="body van-multi-ellipsis--l2">
          笔者读大三, 前端小白一枚, 正在准备春招, 人生第一次面试, 投了头条前端, 总共经历了四轮技术面试和一轮hr面, 不多说, 直接上题&nbsp;一面
        </div>
        <div class="foot">点赞 46 | 浏览 332</div>
      </template>
    </van-cell>
    <van-cell class="article-item" >
      <template #title>
        <div class="head">
          <img src="http://teachoss.itheima.net/heimaQuestionMiniapp/%E5%AE%98%E6%96%B9%E9%BB%98%E8%AE%A4%E5%A4%B4%E5%83%8F%402x.png" alt="" />
          <div class="con">
            <p class="title van-ellipsis">宇宙头条校招前端面经</p>
            <p class="other">不风流怎样倜傥 | 2022-01-20 00-00-00</p>
          </div>
        </div>
      </template>
      <template #label>
        <div class="body van-multi-ellipsis--l2">
          笔者读大三, 前端小白一枚, 正在准备春招, 人生第一次面试, 投了头条前端, 总共经历了四轮技术面试和一轮hr面, 不多说, 直接上题&nbsp;一面
        </div>
        <div class="foot">点赞 46 | 浏览 332</div>
      </template>
    </van-cell>
  </div>
</template>

<script>
export default {
  name: 'ArticlePage',
  data () {
    return {

    }
  },
  methods: {

  }
}
</script>

<style lang="less" scoped>
.article-page {
  margin-bottom: 50px;
  margin-top: 44px;
  .my-nav {
    height: 44px;
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    z-index: 999;
    background: #fff;
    display: flex;
    align-items: center;
    > a {
      color: #999;
      font-size: 14px;
      line-height: 44px;
      margin-left: 20px;
      position: relative;
      transition: all 0.3s;
      &::after {
        content: '';
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        bottom: 0;
        width: 0;
        height: 2px;
        background: #222;
        transition: all 0.3s;
      }
      &.active {
        color: #222;
        &::after {
          width: 14px;
        }
      }
    }
    .logo {
      flex: 1;
      display: flex;
      justify-content: flex-end;
      > img {
        width: 64px;
        height: 28px;
        display: block;
        margin-right: 10px;
      }
    }
  }
}
.article-item {
  .head {
    display: flex;
    img {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      overflow: hidden;
    }
    .con {
      flex: 1;
      overflow: hidden;
      padding-left: 10px;
      p {
        margin: 0;
        line-height: 1.5;
        &.title {
          width: 280px;
        }
        &.other {
          font-size: 10px;
          color: #999;
        }
      }
    }
  }
  .body {
    font-size: 14px;
    color: #666;
    line-height: 1.6;
    margin-top: 10px;
  }
  .foot {
    font-size: 12px;
    color: #999;
    margin-top: 10px;
  }
}
</style>

抽离面经列表项组件,完成全局注册
//main.js
// 注册一个全局组件 ArticleItem
import ArticleItem from '@/components/ArticleItem'
Vue.component(ArticleItem.name, ArticleItem)
//Article.vue
<template>
  <div class="article-page">
    <nav class="my-nav van-hairline--bottom">
      <a
        href="javascript:;"
        >推荐</a
      >
      <a
        href="javascript:;"
        >最新</a
      >
      <div class="logo"><img src="@/assets/logo.png" alt=""></div>
    </nav>
    <article-item></article-item>

  </div>
</template>

<script>
// import ArticleItem from '@/components/ArticleItem.vue'
export default {
  name: 'ArticlePage',
  comments: {
    // ArticleItem
  },
  data () {
    return {

    }
  },
  methods: {

  }
}
</script>

<style lang="less" scoped>
.article-page {
  margin-bottom: 50px;
  margin-top: 44px;
  .my-nav {
    height: 44px;
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    z-index: 999;
    background: #fff;
    display: flex;
    align-items: center;
    > a {
      color: #999;
      font-size: 14px;
      line-height: 44px;
      margin-left: 20px;
      position: relative;
      transition: all 0.3s;
      &::after {
        content: '';
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        bottom: 0;
        width: 0;
        height: 2px;
        background: #222;
        transition: all 0.3s;
      }
      &.active {
        color: #222;
        &::after {
          width: 14px;
        }
      }
    }
    .logo {
      flex: 1;
      display: flex;
      justify-content: flex-end;
      > img {
        width: 64px;
        height: 28px;
        display: block;
        margin-right: 10px;
      }
    }
  }
}

</style>

在其他几个组件like user中一样操作写 即可

//Collect.vue
<template>
  <div class="collect-page">
    <article-item></article-item>
  </div>
</template>

提交git—抽离面经列表项组件,完成全局注册

Vue核心技术与实战--2023.7.4_第64张图片

封装 api 接口-获取文章列表数据

//api/article.js
//封装跟面经相关的接口
import request from '@/utils/request'

export const getArticles = (query) => {
  return request.get('/h5/interview/query', { params: query })
  // get 请求的 query 参数,需要用 params 字段来指定,并且 params包含在一个对象中
}


完成了获取面经列表教据和简单的渲染

?

提交git — 完成了获取面经列表教据和简单的染

Vue核心技术与实战--2023.7.4_第65张图片

面经列表-动态渲染列表

  1. 对于引用类型,不能直接写默认值,必须通过函数的形式返回默认值
//简写   default:()=>({})
default:()={
	return {}
}
  1. 父子组件传参
//ArticleItem.vue
export default {
  name: 'article-item',
  props: {
    data: {
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  methods: {
    clearAllTags (str) {
      return str.replace(/<[^>]+>/ig, '')
    }
  }
}
    • ArticleItem.vue去除全局的标签str.replace(/<[^>]+>/ig,‘’)
//ArticleItem.vue
<template>
    <van-cell class="article-item" >
      <template #title>
        <div class="head">
          <img src="http://teachoss.itheima.net/heimaQuestionMiniapp/%E5%AE%98%E6%96%B9%E9%BB%98%E8%AE%A4%E5%A4%B4%E5%83%8F%402x.png" alt="" />
          <div class="con">
            <p class="title van-ellipsis">{{data.stem}}</p>
            <p class="other">{{data.creator}} |{{data.createdAt}}</p>
          </div>
        </div>
      </template>
      <template #label>
        <div class="body van-multi-ellipsis--l2" >
        {{clearAllTags(data.content)}}
        </div>
        <div class="foot">点赞  {{ data.likeCount }} | 浏览 {{ data.views }}</div>
      </template>
    </van-cell>
</template>
//ArticleItem.vue
methods: {
    clearAllTags (str) {
      return str.replace(/<[^>]+>/ig, '')
    }
  }
提交git— 完成了获取面经列表教据和简单的渲染

处理401=>request.js

// 如果这里发生的是 401 错误,就让用户重新登录:跳转到登录页面
if (error.response.status => 401){
// token 失效 (token错误,不能使用.无效) => token过期
}

//request.js
import axios from 'axios'
import { Toast } from 'vant'
import { getToken } from './storage'
import router from '@/router'
//  axios.create方法会创建一个新的请求实例
// 后续会使用instance 对象去发送请求
const instance = axios.create({
  //  基地址:会在后续的请求中,自动拼接到url上
  baseURL: 'http://interview-api-t.itheima.net'
})

// 添加请求拦截器
instance.interceptors.request.use(function (config) {
  // 在发送请求之前做些什么
  const token = getToken()
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么
  return response
}, function (error) {
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么
  // 统一进行错误处理
  // 给用户提示错误
  // console.log('接口响应结果不是200 段')
// 如果这里发生的是401错误,就让用户重新登陆:跳转到登录页面
  if (error.response.status === 401) {
    // token 失数(token悟说,不能使用,无效) ==> token过期
    // 不能这样写:组件里面才这么写
    // this.$router.push("/login')
    router.push('/Login')
    Toast('token失败,请重新登录')
    return Promise.reject(error)
  }

  Toast(error.response.data.message)
  return Promise.reject(error)
})

// 导出instance,因为: instance是处理过公共基地址和公共错误的请求
export default instance

提交git — 处理401

Vue核心技术与实战--2023.7.4_第66张图片

vant-list组件触底出发事件Vue核心技术与实战--2023.7.4_第67张图片

Vue核心技术与实战--2023.7.4_第68张图片

面经列表-推荐和更新

websocket

Set Map

可选链操作符

Vue核心技术与实战--2023.7.4_第69张图片

Web Worker

原型劫持

  1. 构造函数,原型,实例对象三角图
    Vue核心技术与实战--2023.7.4_第70张图片

Vue核心技术与实战--2023.7.4_第71张图片
2.instance of 运算符在判断对象的类型时非常有用,可以帮助我们判断一个对象是否属于某个特定的类或构造函数的实例
instanceof 运算符会检查 object 的原型链上是否存在 constructor.prototype,如果存在,则返回 true,表示 object 是 constructor 的实例;如果不存在,则返回 false。

console.log(listArray.push ===Array.prototype.push)//true

在这里插入图片描述

  1. 函数写好之后不调用不知道this指向, 只有调用之后才有this指向, 谁调用指向谁
  • 只有.bind() 之后, 不调用也明确this指向

2023.7.8

项目回顾
  1. 开发时和运行时的区别 ⇒ 给用户上线打包使用 ⇒ 运行时
  • development -D开发时
  • -S 运行时
  • less-loader ⇒ 开发时依赖-D
  1. 看文档
  • 看右侧有没有符合的示例 ⇒ 看代码
  • 如何在父组件拿到子组件的实例对象 ⇒ ref
  1. 表单校验 ⇒ 验证信息的正确性 ⇒ 一般失焦时候校验onBlur (onChange)
处理接口失效

前后端并行开发
mock ⇒ 模拟接口数据
调数据要经过 axios (XML / XHR)

//mock/index.js
request.get = function (url, config) {
	// console.log(123)
	if (url === '/h5/interview/query') {
	// console.log(21331)
		return Promise.resolve(articleListData)
	}
}

Vue核心技术与实战--2023.7.4_第72张图片

Promise.resolve
  1. Promise解决了什么问题
  • 回调地狱(回调函数的多层嵌套) ⇒ 嵌套的回调转为扁平的回调
  • 可以把Promise对象理解为一个盒子, 里面放的东西才是我们最关注的: 数据
  • 任何时候 Promise这个盒子都是正确的
    • .then / .catch
    • await ⇒ 异步转同步
  • Promise.resolve() 成功的结果 静态方法 ⇒ 直接产生了一个装了正常数据的Promise对象
  • Promise.reject(错误信息) 失败的结果 ⇒ 直接产生了一个装了异常数据的Promise对象
  • (用来管理异步任务的)

面经渲染

  1. 自定义的组件没有原生事件: click
  2. 报错
    在这里插入图片描述
    调用是异步的, 首次渲染的时候,快于调接口的速度, article此时为空
  • 要么容错
  • 要么用v-html

打包

  1. npm i -g http-server 是一个命令,用于通过npm全局安装一个名为http-server的软件包。该软件包是一个简单的命令行工具,可以帮助你在本地快速启动一个基于HTTP协议的静态文件服务器。

  2. 在vue.config.js中添加代码 publicPath: ‘./’,
    在这里插入图片描述

  3. npm run build

  4. 生成一个dist文件夹 ,单机index直接能打开

首屏优化

  1. 路由懒加载

Vue核心技术与实战--2023.7.4_第73张图片

路由懒加载(Route Lazy Loading)是一种优化前端应用程序性能的方法。 不是一次性将所有路由模块都加载进来
路由懒加载通常与模块打包工具(如Webpack)和前端框架(如React、Vue)一起使用。下面以Vue Router为例,介绍如何实现路由懒加载:

  1. 标准方式加载路由模块:
import Home from './views/Home.vue';
import About from './views/About.vue';
import Contact from './views/Contact.vue';

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About },
  { path: '/contact', component: Contact }
];

// ...

const router = new VueRouter({
  routes
});

在这种方式下,所有路由模块都会在初始加载时一起被打包和加载。

  1. 使用路由懒加载:
const routes = [
  { path: '/', component: () => import('./views/Home.vue') },
  { path: '/about', component: () => import('./views/About.vue') },
  { path: '/contact', component: () => import('./views/Contact.vue') }
];

// ...

const router = new VueRouter({
  routes
});

你可能感兴趣的:(vue.js,前端,javascript)