尚硅谷项目面试题

78 阅读32分钟

1. 前台 PC 项目

有哪些功能模块?

首页
商品搜索列表
商品详情
购物车
登陆与注册
订单交易/结算
支付
个人中心/订单列表

使用了哪些库?

vue
vue-router
vuex
vee-validate
vue-lazyload

axios
mockjs
nprogress
uuidjs

swiper
qrcode
lodash

使用了哪些组件间通信方式

- props
	Pagination组件/Floor组件 等
- 自定义事件
	Pagination组件 ==> 内部改变当前页码后分事件通知父组件, 并传递出最新的页码
- 全局事件总线
	Search组件与Header组件 ==> Search组件内部清除关键字条件时, 通过全局事件总线分发事件通知Header组件清除输入
- vuex
	管理登陆用户信息 / token / 订单 等

axios二次封装

1). 配置通用的基础路径和超时
2). 显示请求进度条
3). 成功返回的数据不再是response, 而直接是响应体数据response.data
4). 统一处理请求错误, 具体请求也可以选择处理或不处理
5). 每次请求都携带一个userTempId请求头, 数据值在state6). 每次请求(已登陆)都携带一个token请求头, 数据值在state7). 对token失效的401错误, 进行处理

如何封装组件?

可以以Pagination组件为例分析

实现静态组件: 模板/样式写好
设计从外部接收的数据: props   => currentPage / total / pageSize / showPageNo
设计内部的数据: data   => myCurrentPage
设计基于props和data的计算属性数据: computed  => totalPages / startEnd
根据props和data数据和computed进行动态显示
更新数据, 更新界面, 通知父组件  =>分发currentChange事件

说说分类导航功能

先动态请求显示一级列表, 鼠标移入某个分类项显示对应的二三级列表(可能要请求)
点击某个分类项, 跳转到搜索页面, 携带分类条件参数

- 使用编程式导航代替声明式导航
  - router-link太多 ==> 创建很多组件对象 ==> 占用内存大, 效率低

- 优化事件处理效率
  - 利用事件委托: event.target
  - 理解事件委托与事件冒泡

- 如何携带点击的分类的数据?
  - event.target得到a标签
  - 利用自定义的data标签属性来保存分类信息

- 对mouseEnter高频事件进行节流处理
  - 使用lodash的throttle进行节流处理
  - 对lodash库实现按需引入

说说搜索功能

准备各种搜索条件
	- category1Id: '', // 一级分类ID
  - category2Id: '', // 二级分类ID
  - category3Id: '', // 三级分类ID
  - categoryName: '', // 分类名称
  - keyword: '', // 关键字
  - trademark: '', // 品牌  "ID:品牌名称"
  - props: [], // 商品属性的数组: ["属性ID:属性值:属性名"] 示例: ["2:6.0~6.24英寸:屏幕尺寸"]
  - order: '1:desc', // 排序方式  1: 综合,2: 价格 asc: 升序,desc: 降序  示例: "1:desc"
  - pageNo: 1, // 当前页码
  - pageSize: 10, // 每页数量
删除搜索关键字条件
	清除输入框中的关键字   ===> 全局事件总线
删除分类条件/关键字条件
	直接发请求 ==> 有问题: 地址栏上的条件参数没有删除
  - 解决:  重新跳转到search, 不再携带要删除条件参数, search组件监视路由的变化, 发请求获取数据

说说购物车功能

- 购物车数据是保存在后台的, 标识是什么?
  - 未登陆: 标识为用户临时ID(userTempId)
    - 第一次访问时前台利用uuid库生成的唯一字符串, 保存保存在local中
    - 每次请求时通过请求头自动携带它(利用请求拦截器)
  - 登陆: 登陆用户的token对应的userId
    - 用户请求登陆时, 服务器端生成并返回给浏览器, 浏览器收到后自动保存到local中
    - 每次请求时通过请求头自动携带它(利用请求拦截器)
- 进入购物车页面 ==> 请求获取购物车列表显示
- 修改购物项数量
	- 提交请求时, 携带商品的skuid和数量(变化的)
	- 对点击进行节流限制
- 删除购物项(一个/多个)
  - 请求接口, 携带一个skuId或多个skuId的数组
  - 参数: skuId的数组   [2,3]
- 勾选购物项(一个/多个)
  - 请求接口, 携带一个skuId或多个skuId的数组 和 是否勾选的标识数据(0/1)

说说注册/登陆/自动登陆的流程

  • 注册流程

    • 前台: 输入注册需要的相关信息(用户名/密码/...), 进行前台表单校验, 如果不通过, 提示错误

    • 前台: 发送注册的ajax请求(post), 携带注册接口需要的相关数据(用户名/密码/...)

    • 后台: 获取到注册请求携带的参数, 去数据库中判断是否已经存在

      • 如果已经存在, 返回提示此用户已存在的提示信息
      • 如果不存在, 保存到数据库, 返回成功的数据
    • 前台: 接收到响应

      • 如果是不成功的数据, 提示
      • 如果是成功的数据, 自动跳转到登陆页面
  • 登陆流程

    • 前台: 输入登陆需要的相关信息(用户名/密码), 进行前台表单校验, 如果不通过, 提示错误
    • 前台: 发送登陆的ajax请求(post), 携带登陆接口需要的相关数据(用户名/密码)
    • 后台: 获取到登陆请求携带的参数, 去数据库中查询看是否存在
      • 如果不存在, 返回登陆失败的信息
      • 如果存在, 生成一个新的token字符串, 将token与用户信息一起返回
    • 前台: 接收到响应
      • 如果是不成功的数据, 提示
      • 如果是成功的数据,
        • 将用户信息和token都保存到vuex中
        • 将token保存到localStorage中 ==> 不保存用户信息
        • 跳转到首页或redirect页面
  • 自动登陆流程

    • 前台: 页面一加载时, 发送请求根据token获取用户信息
    • 后台: 得到请求头中的token值, 解析出里面包含的userId和失效时间,
      • 已经失效了: 返回代表token失效的401的错误响应
      • 还在有效期内: 根据userId查询数据库, 得到用户信息返回

说说项目的优化

  • 懒加载
    • 组件懒加载 => const Home = () => import('./Home.vue') ==> 预加载
    • 图片懒加载 => vue-lazyload
  • 函数节流
    • 鼠标移入显示对应的子分类列表 ==> 避免快速划过每个分类都显示子分类
    • 点击+/-修改购物项数量 ==> 避免快速点击, 增加服务器压力

说一个记忆深刻的开发过程中的问题

  • 问题: 编程式路由跳转到当前路由, 参数不变, 会报出错误

  • 3.1.0版本(2019.8)没这个问题, 3.1.0这后才有这个问题

  • 3.1.0之前: 返回值为undefined

    • push(location)
    • push(location, () => {}, () => {})
    • 3.1.0之后: 如果没有指定回调函数返回promise对象
      • push(location).then(() => {}).catch(() => {})
    • vue-router在3.1.0版本(2019.8)引入了push()的promise的语法, 如果没有通过参数指定回调函数就返回一个promise来指定成功/失败的回调, 且内部会判断如果要跳转的路径和参数都没有变化, 会抛出一个失败的promise
  • 解决: 重写VueRouter原型上的push方法 (比较好)

    • 1). 如果没有指定回调函数, 需要调用原本的push()后catch()来处理错误的promise
    • 2). 如果传入了回调函数, 本身就没问题, 直接调用原本的push()就可以
  • 扩展问题

    • 声明式路由跳转之所有没有问题, 是因为默认传入了成功的空回调函数

2. 后台管理项目

有哪些功能模块?

商品管理功能
	分类查询
	品牌管理
	平台属性管理
	SPU管理
	SKU管理
权限管理
	权限数据(用户/角色/菜单)管理  CRUD
	权限(路由(页面)/按钮的)控制
其它功能:
	优惠管理
	订单管理
	客户管理
	数据报表可视化

使用了哪些库?

vue
vue-router
vuex
element-ui
axios
nprogress
lodash
echarts

使用了哪些组件间通信方式

	自定义事件
		CategorySelector
		SpuForm
	v-model
		el-input
		el-select
	.sync
		控制SpuForm的隐藏
		Dialog/Drawer
	$attrs与v-bind
	$listeners与v-on
		封装HintButton
	ref
		SpuForm/SkuForm数据初始化加载
	插槽
		默认插槽
			通过标签体向子组件传入标签结构
			Table / Form/ Upload/...
		命名插槽
			通过标签体向子组件传入多种不同的标签结构
			Dialog / Upload
		作用域插槽
			决定父组件传递什么标签结构的数据在子组件中
			el-table-column
	vuex
		集中管理状态数据
		多模块编程
            user
            routes
            app

封装了哪些组件?

- HintButton
	- 带标题提示的按钮
	- elementUI组件: ToolTip / Button
	- 使用$attrs与$listeners
	
- CategorySelector
	- 通过Select动态显示三级分类列表
	- 当选择某个分类时, 分发事件通知父组件, 并携带分类ID和级别值

说说登陆和自动登陆的流程

登陆与自动登陆流程.png

注意: 与前台项目的登陆接口不同的返回的数据只有token, 没有用户信息

使用elementUI构建界面相关

模板项目: vue-admin-template    二次开发
element-ui快速搭建项目界面
对element-ui实现按需引入打包
按需引入打包bug: 显示PopConfirm组件的背景是透明的(不是白色)
    原因: 白色背景的样式是PopOver组件提供, 而我们没有对PopOver进行按需引入打包,
    	最终就没有它的样式, 那背景就是透明的
    解决: 引入并注册PopOver

使用深度作用域选择器的理解和应用

    scoped的作用和原理
        作用: scoped样式只能影响当前组件和子组件的根标签
        原理: 加了scoped后产生了哪些变化
            标签: 当前组件的所有原生标签和子组件的根标签都添加了同一个且唯一的data自定义属性
                <div data-v-6777eaa2>
            选择器: 在选择器的最右边加上了当前data自定义属性的选择器 
                ==> 只有可能匹配上当前组件和子组件的根标签(子组件的子标签没有这个属性)
                .box .title[data-v-6777eaa2] {
                   color: red;
                 }
    deep的作用和原理
        deep的作用: 让我们的样式在scoped下也可以影响子组件内部子标签的样式
        使用:
            原生css: >>>
            css预编译器: /deep/ (vue-cli3之前) 或 ::v-deep (vue-cli3及之后)
        原理:
            data自定义属性选择器就会从最右侧转移到deep声明的位置
            ==> 对子组件的内部子标签没有当前data属性的条件限制 ==> 那就可以匹配了
            .box {
            	::v-deep .title {
                    color: red;
                }
            }
            .box[data-v-6777eaa2] .title {
                color: red;
            }
     应用:
     	让抽屉组件Drawer形成垂直滚动
     	修改轮播组件Carousel的分页指示器样式

深克隆的理解和应用

功能: 平台属性修改取消 / 权限控制中
说清楚功能和数据结构
区别浅克隆与深克隆
如何编码实现深克隆
扩展: 自定义深克隆

nextTicknextTick与set两个方法的理解和应用

功能: 列表项动态显示输入框并自动获得焦点
给响应式对象添加新属性: 使用$set()添加edit属性为true  ==> 动态显示input
界面更新后执行: $nextTick()指定回调在Input DOM更新之后执行 => 自动获取焦点
扩展: $nextTick()的原理

路由权限控制的实现

	路由全局前置守卫
	动态添加路由: addRoutes()
	对所有的异步路由进行递归过滤
	
	基于后台数据的权限控制(user/role/permission)
		给用户分配了对应的角色
		给角色分配了权限(按钮和路由)
	权限控制的2个级别: 路由权限和按钮权限
	
	初始化时先只注册不需要登陆或所有用户都可见的常量路由(Login/Home)
	登陆请求成功后/刷新访问项目: 根据token获取用户和权限数据, 并根据权限数据生成权限路由并动态注册
            全局前置守卫: 当有token, 但还没有用户信息就发请求获取用户信息和权限数据
            根据路由权限数据来从所有异步路由的数组中过滤出当前用户权限路由
            动态添加注册用户的权限路由: router.addRoutes()
            将用户的权限路由与常量路由合并用来显示左则导航菜单

路由权限控制中的2个bug

bug1: 模板项目自身代码的问题

    描述: 在权限路由上刷新 页面是空白的
    原因: 动态添加注册的路由只能在后面的路由跳转才可见, 当次跳转看不到
    	而next()是放行当次路由跳转, 自然就看不到刚动态注册的权限路由
    解决:
        // next() // 放行, 没有重新跳转, 看不到最新添加的动态路由
        // next(to.path) // 重新跳转到目标路由, 但丢失了参数(如果有的话)
        next({...to}) // 重新跳转到目标路由, 且参数不会丢失
        NProgress.done() // 结束进度条

bug2: 我们代码的问题

    描述: 如果先用一个A用户登陆, 退出后用B用户登陆, 结果只能看到部分有权限的路由
    原因:  我们在过滤总的异步路由数组中, 过滤掉了内部部分子路由, 另一个用户登陆看不到总的路由数组了
    解决: 深拷贝然后再进行过滤 ==> 不去改变总的异步路由数组

按钮权限控制的实现

    按钮权限数据: 
        在哪?  vuex的user模块的state中  ==> 映射到getters中了 butttons
        结构: ['按钮1权限值', '按钮2权限值']  ==>  ["btn.Attr.add",  "btn.Trademark.add"] 
    如何判断当前用户是否有某个按钮的权限?
        定义判断的函数, 接收特定按钮的权限值, 返回是否有权限的布尔值
        function hasBtnPermission (btnPer)  {
            return store.getters.buttons.includes(btnPer)
        }
    将判断的函数挂载到Vue的原型上, 让所有组件都可见
        Vue.prototype.$hasBP = hasBtnPermission
    在权限组件中利用$hasBP来判断是否显示某个功能按钮
        v-if="$hasBP('btn.Attr.update')"    权限值去菜单管理列表中查看

说一个记忆深刻的开发过程中的问题

HintButton封装的bug
	1. 删除table中的一行, 下一行的hintButton会自动显示文本提示
		原因: 没有table的遍历的key, 用了index作为key, 下一个数据会复用上一个被删除数据的真实DOM
		解决: row-key属性给table的遍历指定key为id
	2. 关闭确定框后, hint-button上的文本提示又会显示
		原因: 按钮在确定框后自动获取焦点 => 显示文本提示
		解决: 在点击按钮时, 让其失去焦点  event.target.blur()
		问题: 如果点击按钮中图标, 不可以
			原因: target此时是图标标签i, 而不是button
			解决: event.currentTarget.blur()

详细说说你实现的某个业务功能的过程

商品平台属性管理
	{id, name: 'xxx', valueList: [{id, valueName: 'yyyy'}]}
	一个平台属性包含一个平台属性名和多个平台属性值, 每个属性值都是包含属性值名称和其它属性的对象
	先根据选择的某个3级分类ID, 请求所有对应的所有平台属性的列表
	点击添加或某个分类的修改按钮进入相同的添加修改界面
	
	点击修改进入时, 要保存要修改的平台属性对象
		问题: 输入修改平台属性值名称后, 不能取消
		原因: 修改界面和列表界面共用一个属性对象
		解决: 保存的不能是列表中的平台属性对象的引用或浅拷贝, 必须是它的深拷贝对象
	
	在编辑平台属性值名称时, 需要实现点击从查看模式变为编辑模式, 也就是从span变为input
		设计: 给每个平台属性值对象添加一个edit属性为true
		问题: 通过row.edit=true后, input没有显示
		原因: 新添加的属性不是响应式的
		解决: 通过$set给row添加edit属性, 值为true
		
		设计: 显示input时, 自动获取焦点
		问题: 通过ref得到input后, 调用focus方法 => input是undefined的错误
		原因: dom还没有更新==>也就是input还没有产生
		解决: 调用$nextTick()在回调函数中获取input调用focus => 回调是在dom更新之后执行的
		
	点击保存发送请求添加或更新平台属性, 在发送请求前, 需要对收集的数据进行一些处理
		过滤掉属性值名称为空的属性值对象
		删除属性值对象中的edit属性

说说你项目中echarts的使用和遇到的问题

- 如何动态显示图表
  - echarts: 
    - 在mounted设置option  ==> 如果是静态数据就已经可以了
    - 监视数据改变, 当数据变化时, 再重新设置带数据option
  - vue-echarts: 
    - 只需要给v-chart指定动态option属性, 通过调用getOption得到配置对象

- 动态显示图表不能正常显示
  - 原因: 初始data数据是undefined         reportData: {}
  - 解决: 给data对应的数据一个空数组的默认值
- 图表显示后不能自适应大小
  - 原因: 窗口大小改变, 且父元素大小改变时, 图表没有重新绘制
  - 解决:
    - echarts: 给window绑定resize监听, 在回调中调用chart对象的resize()方法
    - vue-echarts: 只需要指定autoresize属性即可
- tooltip提示框有时会自动隐藏超出容器区域的部分
  - 解决一: confine: true, // 将 tooltip 框限制在容器的区域内
    - 问题: 档住了鼠标
  - 解决二: position (point, params, dom, rect, size) 返回位置坐标 [x, -40]

3. 前台移动 WEB 应用

有哪些功能模块?

首页
搜索
分类
值得买

用户
商品详情
购物车
下单支付

使用了哪些库?

vue
vue-router
vuex
vee-validate
vue-lazyload => Image
vant ui

axios
mockjs
nprogress
uuidjs
lodash
amfe-flexible
postcss-pxtorem

说说项目的适配如何实现的

  • 实现rem适配
    1. 下载依赖包
yarn add amfe-flexible postcss postcss-pxtorem@5.1.1

说明: 
	amfe-flexible: 将页面宽度
	postcss-pxtorem不能下载最新版本, 与postcss不适配
  • 2. 配置postcss-pxtorem

postcss.config.js

module.exports = {
	plugins: {
		'postcss-pxtorem': {
			rootValue: 37.5, // 设计图页面宽度为375, 划分成10份, 指定1rem=37.5
			propList: ['*'],
		},
	},
};
  • 3. 在入口js中加载amfe-flexible

main.js

import 'amfe-flexible'

项目可说的一些功能技术点

- 移动端rem适配,使用amfe-flexible和postcss-pxtorem插件
- 整体界面使用vant ui库, 并实现按需引入打包
- 通过深度作用域选择器修改vant组件内部样式
- 使用axios请求后台接口, 并对xios进行二次封装, 使用NProgress显示请求进度提示
- 重写路由器的push和replace方法, 解决路由重复跳转报错的bug
- 配置路由的滚动行为, 路由跳转总能停留在顶部, 返回时能停留在原来的位置
- 利用路由的meta配置保存是否显示底部tab的标识, 来控制tab的显示隐藏
- 实现推荐页面与分类频道页的切换显示, 并解决频道标题不能正常选中的相关Bug
- 封装搜索框组件,使用v-model和$attrs&$listeners实现组件间通信
- 使用vant ui的Image组件实现图片懒加载
- 值得买界面: 实现小单元格的自定义轮播效果
- 值得买界面: 使用vue-waterfall-easy插件实现瀑布流分页加载效果

说一个记忆深刻的开发过程中的问题

问题: 首页选中某个频道显示对应的列表页面后, 刷新显示的列表正确, 但会自动选中推荐项
原因:
	推荐的<tab>是静态的, 初始时就会渲染
	其它频道列表的<tab>在初始时不存在, 只有请求得到列表数据后才会产生渲染
	导致的问题 => 
		用来存储标识哪个tab显示的navId在初始值为当前对应的分类Id值
		但由于初始只有推荐的<tab>, navId会自动被赋值为推荐的<tab>的标识name值-1
		等到频道列表的<tab>产生时, 不可能再选中对应的频道<tab>

4. 微信小程序音乐播放器项目

有哪些功能模块?

首页
视频
个人中心
登录
搜索
推荐歌曲
歌曲播放

使用了哪些api/库?

api:
wx.request() 发送请求
wx.navigateTo() 保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。
wx.redirectTo() 关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面。
wx.switchTab() 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
wx.reLaunch() 关闭所有页面,打开到应用内的某个页面
wx.showToast() 显示消息提示框
wx.showLoading() 显示 loading 提示框
wx.showModal() 显示模态对话框
wx.hideLoading() 关闭提示框
wx.setNavigationBarTitl() 动态设置当前页面的标题
wx.setStorageSync() 将数据存储在本地缓存中指定的 key 中
wx.setStorage() 将数据存储在本地缓存中指定的 key 中。
wx.removeStorageSync() 从本地缓存中移除指定 key
wx.removeStorage() 从本地缓存中移除指定 key
wx.getStorage() 异步获取当前storage的相关信息
wx.getStorageSync() 从本地缓存中同步获取指定 key 的内容。
wx.createVideoContext() 视频播放对象的获取
VideoContext.play() 播放
VideoContext.pause() 暂停
VideoContext.stop() 停止
VideoContext.seek() 跳到指定位置
wx.getBackgroundAudioManager() 背景音频对象的获取
BackgroundAudioManager.play()播放音乐
BackgroundAudioManager.pause()暂停音乐
BackgroundAudioManager.stop()停止音乐
BackgroundAudioManager.onPlay()监听背景音频播放事件
BackgroundAudioManager.onPause()监听背景音频暂停事件
BackgroundAudioManager.onEnded()监听背景音频自然播放结束事件
BackgroundAudioManager.onStop()监听背景音频停止事件
BackgroundAudioManager.onTimeUpdate()监听背景音频播放进度更新事件
wx.login() 获取登录凭证
wx.getUserProfile() 获取用户信息

库:
pubsub-js.js 实现页面与页面之间的通信
moment.js 格式化日期

说说项目的适配如何实现的

  • 小程序本身非常支持flex布局,默认使用rpx进行适配
1px=2rpx

wx.request的封装

wx.request 是一个异步的方法
success回调函数的作用域发生了改变,所以this的指向不是当前函数,另外使用promise可以解决异步嵌套的问题
config.js文件:
export default {
  host:'http://localhost:3000'
}
request.js文件:
import config from './config.js'
export default (url, data = {}, method = 'GET') => {
  return new Promise((resolve, reject) => {
    wx.request({
      url: config.host + url,
      data,
      method,
      success: res => {
        resolve(res.data)
      },
      fail: err => {
        reject(err)
      }
    })
  })
}

页面之间如何实现通信

缓存的方式:
wx.setStorage()
wx.setStorageSync()
应用:可以缓存cookie的信息,实现异步请求携带cookie数据

全局唯一实例对象
getApp()
应用:缓存音乐播放/暂停的状态及音乐id数据,从而实现音乐监听相关操作

插件
pubsub-js
应用:自动播放下一曲

eventChannel 事件通道
wx.navigateTo({
  url: 'test?id=1',
  events: {
    // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据
    acceptDataFromOpenedPage: function(data) {
      console.log(data)
    },
    someEvent: function(data) {
      console.log(data)
    }
    ...
  },
  success: function(res) {
    // 通过eventChannel向被打开页面传送数据
    res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' })
  }
})
Page({
  onLoad: function(option){
    console.log(option.query)
    const eventChannel = this.getOpenerEventChannel()
    eventChannel.emit('acceptDataFromOpenedPage', {data: 'test'});
    eventChannel.emit('someEvent', {data: 'test'});
    // 监听acceptDataFromOpenerPage事件,获取上一页面通过eventChannel传送到当前页面的数据
    eventChannel.on('acceptDataFromOpenerPage', function(data) {
      console.log(data)
    })
  }
})

微信小程序中事件的理解

事件是视图层到逻辑层的通讯方式。
事件可以将用户的行为反馈到逻辑层进行处理。
事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数。
事件对象可以携带额外信息,如 id, dataset, touches。

事件分为冒泡事件和非冒泡事件:

冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递。
非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。

事件处理函数中的event参数中可以携带相关的参数,如:id/data-
id的方式携带的数据的类型最终为string类型
data-的方式可以直接存储数值类型的数据,使用的时候无需转换


自定义组件的理解

微信小程序中自定义组件通常存放在components目录中
自定义组件component和页page的区别在于xxx.js文件

组件的js文件中代码:
Component({
  data: {}, 自身使用的数据
  properties: {  }, 某个页面传递进来的数据
  methods: { } 当前组件所需的方法
})

页page的js文件中代码:
Page({
data:{},
onLoad(){},
....
})



模版的理解

WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。
定义模板
使用 name 属性,作为模板的名字。然后在<template/>内定义代码片段
<template name="msgItem">
  <view>
    <text> {{index}}: {{msg}} </text>
    <text> Time: {{time}} </text>
  </view>
</template>
使用模板
使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入
<template is="msgItem" data="{{...person}}"/>
Page({
  data: {
    person: {
     name:'小甜甜',
     age:20
    }
  }
})

页面中使用模版:
1. 先引入模版的结构文件
<import src="item.wxml"/>
<template is="item" data="{{text: 'forbar'}}"/>
2. 然后引入模版的样式文件
 在页面的.wxss文件中引入
@import 'item.wxss'

生命周期的理解

微信小程序的生命周期分为:
App的生命周期/Page的生命周期/Component的生命周期

App的生命周期如下:
onLaunch(){}	生命周期回调——监听小程序初始化。	
onShow(){}		生命周期回调——监听小程序启动或切前台。	
onHide(){}		生命周期回调——监听小程序切后台

Page的生命周期
onLoad(){}			生命周期回调—监听页面加载
onShow(){}			生命周期回调—监听页面显示
onReady(){}			生命周期回调—监听页面初次渲染完成
onHide(){}			生命周期回调—监听页面隐藏
onUnload(){}		生命周期回调—监听页面卸载

Component的生命周期
created(){}		组件生命周期函数-在组件实例刚刚被创建时执行,注意此时不能调用 setData )	
attached(){}	组件生命周期函数-在组件实例进入页面节点树时执行)	
ready(){}		组件生命周期函数-在组件布局完成后执行)	
moved(){}		组件生命周期函数-在组件实例被移动到节点树另一个位置时执行)	
detached(){}	组件生命周期函数-在组件实例被从页面节点树移除时执行)

WXML语法

数据绑定使用 Mustache 语法(双大括号)将变量包起来
<view>{{msg}}</view>

组件属性(需要在双引号之内)
<view id="item-{{id}}"> </view>

控制属性(需要在双引号之内)
<view wx:if="{{condition}}"> </view>

关键字(需要在双引号之内)
true:boolean 类型的 true,代表真值。
false: boolean 类型的 false,代表假值。
<checkbox checked="{{false}}"> </checkbox>
特别注意:不要直接写 checked="false",其计算结果是一个字符串,转成 boolean 类型后代表真值

列表渲染
wx:for
在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为 index,数组当前项的变量名默认为 item
<view wx:for="{{array}}">
  {{index}}: {{item.message}}
</view>


使用 wx:for-item 可以指定数组当前元素的变量名,
使用 wx:for-index 可以指定数组当前下标的变量名:
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName">
  {{idx}}: {{itemName.message}}
</view>


block wx:for
类似 block wx:if,也可以将 wx:for 用在<block/>标签上,以渲染一个包含多节点的结构块。例如:
<block wx:for="{{[1, 2, 3]}}">
  <view> {{index}}: </view>
  <view> {{item}} </view>
</block>

wx:key
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。
wx:key 的值以两种形式提供
字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。


uni-app框架的理解


uni-app 是一个使用 Vue.js (opens new window)开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、AndroidWeb(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。


5. 微信小程序相关面试题

谈谈你对微信小程序的理解

1. 2017年度百度百科十大热词之一
2. 张小龙对其的定义是无需安装,用完即走,实际上是需要安装的,只不过小程序的体积特别小, 下载速度很快,用户感觉不到下载的过程
3. 2017年1月9日0点上线
4.	没有DOM,组件化开发
5.	体积小,单个压缩包体积不能大于2M,否则无法上线
6.	小程序的四个重要的文件
	a)	*.js 
	b)	*.wxml ---> view结构 ----> html
	c)	*.wxss ---> view样式 -----> css
	d)	*. json ----> view 数据 -----> json文件
7.	小程序适配方案: rpx (responsive pixel响应式像素单位)

微信小程序的分包

1 什么要分包
    1.	小程序要求压缩包体积不能大于2M,否则无法发布
    2.	实际开发中小程序体积如果大于2M就需要使用分包机制进行发布上传
    3.	分包后可解决2M限制,并且能分包加载内容,提高性能
    4.	分包后单个包的体积不能大于2M
    5.	分包后所有包的体积不能大于20M
2 分包形式
    1.	常规分包
    2.	独立分包
    3.	分包预下载
3 常规分包
    1.	开发者通过在 app.json subpackages 字段声明项目分包结构
    2.	特点: 
        a)	加载小程序的时候先加载主包,当需要访问分包的页面时候才加载分包内容
        b)	分包的页面可以访问主包的文件,数据,图片等资源
        c)	主包: 
    3.	主包来源: 除了分包以外的内容都会被打包到主包中
    4.	通常放置启动页/tabBar页面
4 独立分包
    1.	设置independent为true
    2.	特点: 
        a)	独立分包可单独访问分包的内容,不需要下载主包
        b)	独立分包不能依赖主包或者其他包的内容
    3.	使用场景
        a)	通常某些页面和当前小程序的其他页面关联不大的时候可进行独立分包
        b)	如:临时加的广告页 || 活动页
5 分包预下载
    1.	配置
        a)	app.json中设置preloadRule选项
        b)	key(页面路径): {packages: [预下载的包名 ||  预下载的包的根路径])}


vue和小程序的区别

vue是渐进式的框架
小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播
1. vue的生命周期和小程序的生命周期对比
2. vue中的数据代理和小程序中的是否有数据代理进行对比
3. 组件化的对比
4. vue中的组件通信,小程序中的页面之间通信
5. vue中的路由和小程序的路由对比
6. vue组件中的方法定义位置和小程序中的方法定义位置对比
7. 异步请求的方式(axios/wx.request)
8. vue需要ie8以上浏览器,小程序在安卓/ios系统上

小程序部分常见面试题

提高微信小程序的应用速度的常见方式有哪些?
    1. 提高页面加载速度
    2. 用户行为预测
    3. 减少默认data的大小
    4. 组件化方案
    5. 分包预下载

小程序与原生App相比优缺点?
优点:
基于微信平台开发,微信本身自带的流量是最大的优势,无需安装,只要打开微信就能用,不占用用户手机内存,体验好,开发周期短,一般最多一个月可以上线完成,开发所需的资金少,所需资金是开发原生APP一半不到,小程序名称是唯一性的,在微信的搜索里权重很高
容易上手,只要之前有HTML+CSS+JS基础知识,写小程序基本上没有大问题;当然如果了解ES6+CSS3则完全可以编写出即精简又动感的小程序;
基本上不需要考虑兼容性问题,只要微信可以正常运行的机器,就可以运行小程序;
发布、审核高效,基本上上午发布审核,下午就审核通过,升级简单,而且支持灰度发布;
开发文档比较完善,开发社区比较活跃;
新增webview组件,可以展示网页
支持插件式开发,一些基本功能可以开发成插件,供多个小程序调用;
缺点:
1.局限性很强,(比如压缩体积不能超过2M。样式单一。小程序的部分组件已经是成型的了,样式不可以修改。例如:幻灯片、导航。)只能依赖于微信依托于微信,无法开发后台管理功能。
2.不利于推广推广面窄,不能分享朋友圈,只能通过分享给朋友,附近小程序推广。其中附近小程序也受到微信的限制
3.后台调试麻烦,因为API接口必须https请求,且公网地址,也就是说后台代码必须发布到远程服务器上;当然我们可以修改host进行dns映射把远程服务器转到本地,或者开启tomcat远程调试;不管怎么说终归调试比较麻烦。
4.前台测试有诸多坑,最头疼莫过于模拟器与真机显示不一致
5.小程序中对js使用做了很多限制,不能使用:new FunctionevalGenerator,不能操作cookie,不能操作DOM;

原生App优点:

1、原生的响应速度快
2、对于有无网络操作时,譬如离线操作基本选用原生开发
3、需要调用系统硬件的功能(摄像头、方向传感器、重力传感器、拨号、GPS、语音、短信、蓝牙等功能)
4、在无网络或者若网的情况下体验好。

缺点:
开发周期长,开发成本高
需要下载
简述微信小程序原理?
  答:微信小程序采用JavaScriptWXMLWXSS三种技术进行开发,从技术讲和现有的前端开发差不多,但深入挖掘的话却又有所不同。
JavaScript:首先JavaScript的代码是运行在微信App中的,并不是运行在浏览器中,因此一些H5技术的应用,需要微信App提供对应的API支持,而这限制住了H5技术的应用,且其不能称为严格的H5,可以称其为伪H5,同理,微信提供的独有的某些APIH5也不支持或支持的不是特别好。
WXMLWXML微信自己基于XML语法开发的,因此开发时,只能使用微信提供的现有标签,HTML的标签是无法使用的。
WXSSWXSS具有CSS的大部分特性,但并不是所有的都支持,而且支持哪些,不支持哪些并没有详细的文档。
微信的架构,是数据驱动的架构模式,它的UI和数据是分离的,所有的页面更新,都需要通过对数据的更改来实现。
小程序分为两个部分webview和appService。其中webview主要用来展现UI,appService有来处理业务逻辑、数据及接口调用。它们在两个进程中运行,通过系统层JSBridge实现通信,实现UI的渲染、事件的处理

分析下微信小程序的优劣势?
优势:
1、无需下载,通过搜索和扫一扫就可以打开。
2、良好的用户体验:打开速度快。
3、开发成本要比App要低。
4、安卓上可以添加到桌面,与原生App差不多。
5、为用户提供良好的安全保障。小程序的发布,微信拥有一套严格的审查流程, 不能通过审查的小程序是无法发布到线上的。
劣势:
1、限制较多。页面大小不能超过1M。不能打开超过5个层级的页面。
2、样式单一。小程序的部分组件已经是成型的了,样式不可以修改。例如:幻灯片、导航。
3、推广面窄,不能分享朋友圈,只能通过分享给朋友,附近小程序推广。其中附近小程序也受到微信的限制。
4、依托于微信,无法开发后台管理功能。

小程序的发布流程(开发流程)
注册微信小程序账号
获取微信小程序的 AppID
下载微信小程序开发者工具
创建demo项目
去微信公众平台配置域名
手机预览
代码上传
提交审核
小程序发布
 

webview中的页面怎么跳回小程序中
首先,需要在你的html页面中引用一个js文件。
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.0.js"></script>
然后为你的按钮标签注册一个点击事件:
    $(".kaiqi").click(function(){undefined
        wx.miniProgram.redirectTo({url: '/pages/indexTwo/indexTwo'})
    });
这里的redirectTo跟小程序中的wx.redirectTo()跳转页面是一样的,会关闭当前页跳转到页面。
你也可以替换成navigateTo,跳转页面不会关闭当前页。
使用webview直接加载要注意哪些事项?
一、必须要在小程序后台使用管理员添加业务域名;
二、h5页面跳转至小程序的脚本必须是1.3.1以上;
三、微信分享只可以都是小程序的主名称了,如果要自定义分享的内容,需小程序版本在1.7.1以上;
四、h5的支付不可以是微信公众号的appid,必须是小程序的appid,而且用户的openid也必须是用户和小程序的。
小程序授权登录流程。
(授权,微信登录获取code,微信登录,获取 iv , encryptedData 传到服务器后台,如果没有注册,需要注册。)
(授权,微信登录获取code,微信登录,获取 iv , encryptedData 传到服务器后台,如果没有注册,需要注册。)

小程序支付如何实现?
1、小程序注册,要以公司的以身份去注册一个小程序,才有微信支付权限;
2、绑定商户号。
3、在小程序填写合法域
4.调用wx.login()获取appid
5.调用
wx.requestPayment(
{
'timeStamp': '',//时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
'nonceStr': '',//随机字符串,长度为32个字符以下。
'package': '',//统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=*
'signType': 'MD5',//签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
'paySign': '',//签名,具体签名方案参见微信公众号支付帮助文档;
'success':function(res){},//成功回调
'fail':function(res){},//失败
'complete':function(res){}//接口调用结束的回调函数(调用成功、失败都会执行)
})

小程序还有哪些功能?
   客服功能,录音,视频,音频,地图,定位,拍照,动画,canvas

微信小程序与H5的区别?

第一条是运行环境的不同
传统的HTML5的运行环境是浏览器,包括webview,而微信小程序的运行环境并非完整的浏览器,是微信开发团队基于浏览器内核完全重构的一个内置解析器,针对小程序专门做了优化,配合自己定义的开发语言标准,提升了小程序的性能。
第二条是开发成本的不同
只在微信中运行,所以不用再去顾虑浏览器兼容性,不用担心生产环境中出现不可预料的奇妙BUG
第三条是获取系统级权限的不同
系统级权限都可以和微信小程序无缝衔接
第四条便是应用在生产环境的运行流畅度
长久以来,当HTML5应用面对复杂的业务逻辑或者丰富的页面交互时,它的体验总是不尽人意,需要不断的对项目优化来提升用户体验。但是由于微信小程序运行环境独立

小程序怎么实现下拉刷新?
方式1:
通过在 app.json 中, 将 "enablePullDownRefresh": true, 开启全局下拉刷新。
或者通过在 组件 .json , 将 "enablePullDownRefresh": true, 单组件下拉刷新。
方式2:
scroll-view :使用该滚动组件 自定义刷新,通过 bindscrolltoupper 属性, 当滚动到顶部/左边,会触发 scrolltoupper事件,所以我们可以利用这个属性,来实现下拉刷新功能。