diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7dce620..789b7b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,4 +29,4 @@ jobs: # 工作流 BASE_BRANCH: master # 要部署的文件夹所在的分支. BRANCH: gh-pages # 部署到的分支 FOLDER: docs/.vuepress/dist # 要部署的文件夹. - BUILD_SCRIPT: npm install && npm run build && cd docs/.vuepress/dist && echo 'b.evanblogweb.com' > CNAME # 部署前要执行的命令 #&& npm run list + BUILD_SCRIPT: npm install && npm run build && && ls && cd docs/.vuepress/dist && echo 'b.evanblogweb.com' > CNAME # 部署前要执行的命令 #&& npm run list diff --git a/docs/.vuepress/config/nav.js b/docs/.vuepress/config/nav.js index ff0f916..09c10ca 100644 --- a/docs/.vuepress/config/nav.js +++ b/docs/.vuepress/config/nav.js @@ -7,13 +7,12 @@ module.exports = [ //{text: 'vue', link: '/web/vue/'}, //{text: 'TypeScript', link: '/web/TypeScript/'}, //{text: 'ES6', link: '/web/ES6/'}, - {text: 'CSS/HTML', link: '/web/CSS-HTML/01.flex布局基础'}, ] }, { - text: 'python', + text: '页面', items: [ - {text: 'python', link: '/python/'} + {text: 'HTML/CSS', link: '/ui/00.flex布局语法'}, ] }, {text: '技术杂谈',items:[ @@ -22,11 +21,15 @@ module.exports = [ {text: 'GitHub高级搜索技巧', link: '/other/github'}, {text: 'Markdown使用教程', link: '/other/markdown'}, {text: 'npm使用教程', link: '/other/npm'}, + {text: 'yaml教程', link: '/other/yaml'}, ]}, {text: '学习',items:[ {text: '学习效果低,忘性很大怎么办?',link: '/other/LearningAndMemory'}, {text: '学习网站分享', link: '/other/study'}, ]}, + {text: '学习笔记',items:[ + {text: '小程序笔记',link: '/other/note/01.小程序笔记'}, + ]}, {text: '面试',items:[ {text: '面试问答集锦', link: '/other/interview'}, ]}, diff --git a/docs/.vuepress/config/sidebar.js b/docs/.vuepress/config/sidebar.js index 87ef7ca..2b8b840 100644 --- a/docs/.vuepress/config/sidebar.js +++ b/docs/.vuepress/config/sidebar.js @@ -12,21 +12,24 @@ module.exports = { // 侧边栏 ['JavaScript/06.多种数组去重性能对比','多种数组去重性能对比'], ] }, + ], + '/ui/': [ { - title: 'CSS/HTML', + title: 'HTML/CSS', collapsable: false, children: [ - ['CSS-HTML/01.flex布局基础','flex布局基础'], - ['CSS-HTML/02.flex布局案例-骰子','flex布局案例-骰子'], - ['CSS-HTML/03.flex布局案例-网格布局','flex布局案例-网格布局'], - ['CSS-HTML/04.flex布局案例-圣杯布局','flex布局案例-圣杯布局'], - ['CSS-HTML/05.flex布局案例-输入框布局','flex布局案例-输入框布局'], - ['CSS-HTML/06.CSS3之transform过渡','CSS3之transform过渡'], - ['CSS-HTML/07.CSS3之animation动画','CSS3之animation动画'], + ['00.flex布局语法','flex布局语法'], + ['01.flex布局案例-基础','flex布局案例-基础'], + ['02.flex布局案例-骰子','flex布局案例-骰子'], + ['03.flex布局案例-网格布局','flex布局案例-网格布局'], + ['04.flex布局案例-圣杯布局','flex布局案例-圣杯布局'], + ['05.flex布局案例-输入框布局','flex布局案例-输入框布局'], + ['06.CSS3之transform过渡','CSS3之transform过渡'], + ['07.CSS3之animation动画','CSS3之animation动画'], ] }, ], - '/other/': [// 针对不同页面设置不同侧边栏 + '/other/': [ { title: '技术', collapsable: false, //是否可折叠,可选的,默认true @@ -36,9 +39,9 @@ module.exports = { // 侧边栏 ['github','GitHub高级搜索技巧'], ['markdown','Markdown使用教程'], ['npm','npm使用教程'], + ['yaml','yaml教程'], ] }, - { title: '学习', collapsable: false, @@ -47,7 +50,13 @@ module.exports = { // 侧边栏 ['study','学习网站分享'], ] }, - + { + title: '学习笔记', + collapsable: false, + children: [ + ['note/01.小程序笔记','小程序笔记'], + ] + }, { title: '面试', collapsable: false, diff --git a/docs/.vuepress/public/img/other.png b/docs/.vuepress/public/img/other.png index 03d7224..87f8098 100644 Binary files a/docs/.vuepress/public/img/other.png and b/docs/.vuepress/public/img/other.png differ diff --git a/docs/.vuepress/public/img/other2.png b/docs/.vuepress/public/img/other2.png new file mode 100644 index 0000000..c6fe28a Binary files /dev/null and b/docs/.vuepress/public/img/other2.png differ diff --git a/docs/.vuepress/public/img/ui.png b/docs/.vuepress/public/img/ui.png new file mode 100644 index 0000000..3ca9721 Binary files /dev/null and b/docs/.vuepress/public/img/ui.png differ diff --git a/docs/.vuepress/public/img/ui2.png b/docs/.vuepress/public/img/ui2.png new file mode 100644 index 0000000..d9836a2 Binary files /dev/null and b/docs/.vuepress/public/img/ui2.png differ diff --git a/docs/.vuepress/public/img/web2.png b/docs/.vuepress/public/img/web2.png new file mode 100644 index 0000000..c11cac9 Binary files /dev/null and b/docs/.vuepress/public/img/web2.png differ diff --git a/docs/.vuepress/theme/components/Home.vue b/docs/.vuepress/theme/components/Home.vue index 2b43a35..debe857 100644 --- a/docs/.vuepress/theme/components/Home.vue +++ b/docs/.vuepress/theme/components/Home.vue @@ -157,6 +157,7 @@ export default { .feature { max-width: 100%; padding: 0 2.5rem; + margin: 0 auto; } } } diff --git a/docs/other/note/01.小程序笔记.md b/docs/other/note/01.小程序笔记.md new file mode 100644 index 0000000..35094f5 --- /dev/null +++ b/docs/other/note/01.小程序笔记.md @@ -0,0 +1,2748 @@ +## 基础课程部分 + +#### 微信公众平台 + + + +注册时可选择类型:订阅号、服务号、小程序、企业微信 + +**每个邮箱仅能注册一个小程序。** + + + +个人类型小程序:无法使用微信支付、无法使用卡包功能 + +##### 小程序文档API + +[小程序开发文档](https://developers.weixin.qq.com/miniprogram/dev/framework/quickstart/) + +##### 微信开放社区 + +[微信开发社区](https://developers.weixin.qq.com/community/develop/question) + + + +#### 目录说明 + +**默认目录** + +pages-----------------------页面相关 + +​ index ----------------- 首页文件夹 + +​ index.js ------------首页js + +​ index.json---------首页配置 + +​ index.wxml-------首页html + +​ index.wxss--------首页css + + + +utils------------------------工具相关 + + + +app.js ----------------------项目总js + +app.json-------------------全局配置( 页面路由以及头部、底部导航的配置等) + +app.wxss -----------------项目总样式css + +project.config.json ----项目配置 + + + +**代码构成** + +.json :配置文件,以json格式存储配置 + +​ 项目中有三种配置:项目配置(project.config.json)、全局配置(app.json)、页面配置(index.json) + +.wxml: 相当于html文件 + +.wxss: 相当于css + +.js : 就是js + + + +#### 文件说明 + +**project.config.json项目配置 部分代码说明** + +setting:{ + +urlCheck 是否检测安全的域名 + +es6 是否把es6转es5 + +postcss 是否把css样式自动补全 + +minified 是否压缩 + +} + + + +**app.json 全局配置** + +[全局配置API](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html) + + + +**wxml 相关介绍** + +[wxmlAPI](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/) + +```html +{{motto}} + +循环渲染 + + {{index}} {{item}} + + +改变for循环item和index的名称 + + {{inx}} {{data}} + + +条件渲染 (类似vue的v-if、v-else) +已登录 +请登录 + +条件显示(类似vue的v-show) + + +绑定点击事件 + + +Page({ + tapName: function(event) { + console.log(event) + } +}) +... + +``` + + + +**wxss 相关介绍** + +[wxssAPI](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html) + +尺寸单位:rpx,根据屏幕宽度自适应。 + +引入外部wxss:@import ’...‘ + + + +**js相关介绍** + +WXS(WeiXin Script)是小程序的一套脚本语言 + +[wxsAPI](https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxs/) + +绑定点击事件 + +```html + +{{count}} +``` + +```js +Page({ + data: { + count: 0 + }, + onTapHandler: function() { + this.setData({ + count: this.data.count++ + }) + } +}) +``` + +##### 阻止事件冒泡 + +把绑定方式 bindtap 换成 catchtap 即可。 + + + + + +#### 第三方库 + +**WeUI** + +weUI是一套同微信原生视觉体验一致的基础样式库 + + + +**iView Weapp** + +一套高质量的微信小程序UI组件库 + + + +**Vant Weapp** + +轻量、可靠的小程序UI组件库 + + + +#### 云开发 + + + +**小程序传统开发模式** + +客户端 -----> 服务端(后端代码、数据库)------> 运维(DB维护、文件存储、内容加速、网络防护、容器服务、负载均衡、安全加固等...) + + + +**小程序云开发模式** + +客户端 -----> 云开发(云函数、云数据库、云存储) + + + +**传统开发 VS 云开发** + +开发效率低 Serverless(无服务) + +运维成本高 开发者更关注业务逻辑 + + + +**无服务(Serverless)开发是未来的发展趋势** + + + + + +##### **云开发三大基础能力** + +##### 云函数 + +(相当于传统开发中的后台接口) + +获取appid、获取openid、生成分享图、调用腾讯云SDK ... + + + +##### 云数据库 + +数据的增、删、改、查 ... + + + +##### 云存储 + +管理文件、上传文件、下载文件、分享文件 ... + + + +**每个小程序账号可免费创建两个环境,建议:开发环境、生成环境** + + + + + +#### 云数据库能力 + +云开发提供了一个json数据库,提供2GB免费存储空间。 + + + +#### 数据类型 + +String 字符串 + +Number 数字 + +Object 对象 + +Array 数组 + +Boolean 布尔值 + +GeoPoint 地理位置点 + +Date 时间 (精确到毫秒ms,客户端时间) + +Null 空 + + + +#### 操作云数据库 + +小程序控制(读写数据库受权限限制) + +云函数控制(拥有所有读写数据库的权限) + +控制台控制(拥有所有读写数据库的权限) + + + +#### 云数据库权限管理 + +仅创建者可写,所有人可读 (适合于文章) + +仅创建者可读写 (适用于私密内容) + +仅管理端可写,所有人可读(适用于商品信息) + +仅管理端可读写(适用于后台敏感数据) + + + +#### 操作云数据库 + +```js +//初始化数据库 +const db = wx.cloud.database() // 小程序端初始化数据库,如果在云函数端不需要加wx + +//切换环境(开发环境/生产环境) +const testDB = wx.cloud.database({ + env: 'test' +}) +``` + + + +## 实战课程部分 + + + +#### serverless(无服务) + +概念:函数即服务,当需要后端服务的时候,不需要关心后端的IP地址、域名,只需要像调用普通函数一样既可以实现调用。 + + + +#### 云开发优势 + +快速上线、专注核心业务、独立开发一个完整的微信小程序、不需要学习新的语言,只需要会javascript、无需运维, 节约成本、数据安全、 + + + +#### 云开发提供能力 + +云函数:在云端运行的代码,微信私有协议天然鉴权 (理解:相当于后端部分) + +云数据库:一个既可以在小程序端操作又可以在云函数中操作的JSON数据库 + +云存储:在云端存储文件,可以在云端控制台可视化管理 + +云调用:基于云函数免鉴权使用小程序开放接口的能力(比如说给用户推送消息等) + +HTTP API:使用HTTP API开发者可在已有服务器上访问云资源,实现与云开发的互通(作用:对原有传统模式下开发的小程序,可以与云开发进行互通) + + + +#### appID + +每个小程序唯一的id + + + +#### 云开发项目默认目录结构 + + + +cloudfunctions ----------------------------云函数 + +​ callback ---------------------------------- 回调函数 + +​ config.json --------------------------- + +​ index.js -------------------------------- + +​ package.json ------------------------ + +​ echo ---------------------------------------- + +​ login ---------------------------------------- + +​ openapi ----------------------------------- + +miniprogram ------------------------------- 小程序 + +​ images ------------------------------------- 图片 + +​ pages --------------------------------------- 页面 + +​ style ----------------------------------------- 样式 + +​ app.js --------------------------------------- 项目js + +​ app.json ----------------------------------- 全局配置 + +​ app.wxss ---------------------------------- 项目样式 + +​ sitemap.json ----------------------------- (小程序SEO相关) + +project.config.json ----------------------- 项目配置 + + + +#### 云开发环境 + +云开发可创建两个环境,建议一个为开发环境,一个为生产环境 + + + +#### 开发前的准备 + +开发工具 > 右上角详情 > 本地设置 > 调试基础库 设置为最新版本 + +app.js > wx.cloud.init > env 设置环境ID + + + +#### project.config.json 文件说明 + +miniprogramRoot 小程序前端代码目录 + +cloudfunctionRoot 云函数代码目录 + + + +#### app.json + +pages 设置页面 ,设置后会自动在pages目录下生成相应的目录和文件 + +设置底部导航按钮: + +```json +"tabBar": { + "color": "#474747", // 文字颜色 + "selectedColor": "#d43c43", // 文字选中颜色 + "list": [{ // 按钮列表,2-5项 + "pagePath": "pages/playlist/playlist", // 按钮对应页面 + "text": "音乐", // 文字 + "iconPath": "images/music.png", // 图标路径 + "selectedIconPath": "images/music-actived.png" // 选中图标的路径 + }, + { + "pagePath": "pages/blog/blog", + "text": "发现", + "iconPath": "images/blog.png", + "selectedIconPath": "images/blog-actived.png" + }, + { + "pagePath": "pages/profile/profile", + "text": "我的", + "iconPath": "images/profile.png", + "selectedIconPath": "images/profile-actived.png" + }] + } +``` + +> 图标来自于 +> +> 阿里巴巴图标库,包含矢量图标、字体图标、字体等 + + + + + +#### 代码规范 + +很多公司借鉴的代码规范: + + + + + +#### 《音乐》页面开发 + +```html + + + + + + + + + +``` + + + +#### 自定义组件 + +**创建组件** + +创建目录 components > 组件目录名称 > 右键 `新建Component` + +##### **引入组件** + +在page的json文件中: + +```json +{ + "usingComponents": { + "x-playlist":"/components/playlist/playlist" + } +} +``` + +在page的wxml中: + +```html + +``` + + + +**页面引入组件以及组件内部在引用子组件的方法是一样的,同样需要设置json文件。** + + + + + +##### **组件传值** + +父组件中:在引入组件的时候自定义属性名称,并把数据传入子组件 + +```html + + +``` + +子组件中: +子组件的js文件: + +```js + /** + * 组件的属性列表 + */ + properties: { + playlist:{ // 接收父组件传输的数据 + type: Object // 数据类型 + } + }, +``` + +子组件的wxml文件可直接引入数据{{playlist}} + + + + + +#### wx:key 的使用 + +key的值不建议使用{{index}},因为当数据发生变化会dom结构产生变化时,使用index的地方不会随之变化。 + +可以使用数据内部每项不一样的一个数值,如id + +```html + 这里url不需要双大括号,如使用index则需要{{}} + + + + + + + + + + + + +``` + + + +#### async/await 语法 + +目前,在云函数里,由于 Node 版本最低是 8.9,因此是天然支持 async/await 语法的。而在小程序端则不然。在微信开发者工具里,以及 Android 端手机(浏览器内核是 QQ浏览器的 X5),async/await是天然支持的,但 iOS 端手机在较低版本则不支持,因此需要引入额外的 文件。 + +可把这个 [runtime.js](https://github.com/xiecheng328/miniprogram/blob/master/regenerator/runtime.js) 文件引用到有使用 async/await 的文件当中。 + +```js +// 注意,必须命名为 regeneratorRuntime +import regeneratorRuntime from '../../utils/runtime.js' +``` + + + +#### 云函数的使用 + +cloudfunctions目录 右键 `新建 Node.js 云函数` > 输入目录名 `getPlaylist` + + + +在云函数中向第三方服务器发送请求要依赖第三方库 + +**安装依赖包** + +云函数目录 `getPlaylist` 右键 `在终端打开` 打开命令行 输入命令: + +```sh +npm install --save request +npm install --save request-promise +``` + +github request-promise: + + + +然后写相应代码 + +```js +// 云函数入口文件 +const cloud = require('wx-server-sdk') + +cloud.init() + +const rp = require('request-promise') // 需安装依赖包 + +const URL = 'http://musicapi.xiecheng.live/personalized' + +// 云函数入口函数 +exports.main = async (event, context) => { + const playlist = await rp(URL).then((res) => { + return JSON.parse(res).result + }) + console.log(playlist) +} +``` + +写完代码,云函数目录 `getPlaylist` 右键 `上传并部署:云端安装依赖(不上传node_modules)` 进行上传部署代码到云端,等待上传成功,打开云开发控制台即可看到已经上传的云函数,并可对云函数进行测试。 + + + +##### 数据库操作 + +数据库> 创建集合 > playlist + +```js +// 云函数入口文件 +const cloud = require('wx-server-sdk') + +cloud.init() + +const db = cloud.database() // 初始化数据库,如果在小程序端初始化数据库需要在前面加wx. + +const rp = require('request-promise') // 需安装依赖包 + +const URL = 'http://musicapi.xiecheng.live/personalized' // 第三方服务器地址(老师从网易云获取的数据部署在其服务器,每天的数据会更新) + +const playlistCollection = db.collection('playlist') // 获取到数据库playlist集合 + +const MAX_LIMIT = 10 // 定义常量,获取数据库条数最大的限制 + +// 云函数入口函数 +exports.main = async (event, context) => { + /** + * 注: + * - 关于数据库的操作都是异步操作,都需添加await关键字 + * - console.log 打印在云开发控制台 云函数测试内查看 + * - 单次获取数据库数据有条数限制,云函数端最多获取100条,小程序端最多获取20条 + */ + + // const list = await playlistCollection.get() // 获取数据库集合的数据 (因为有条数限制,不直接用此方法) + + // 突破条数限制 (为了读取到全部数据然后与第三方服务器获取的数据进行对比去重) + const countResult = await playlistCollection.count() // 获取数据总条数 返回为对象 + const total = countResult.total // 取得总条数 + const batchTimes = Math.ceil(total / MAX_LIMIT) + const tasks = [] + for(let i = 0; i < batchTimes; i++) { + let promise = playlistCollection.skip(i * MAX_LIMIT).limit(MAX_LIMIT).get() // 从第 skip 条开始取,最多取 limit 条数据 + tasks.push(promise) + } + let list = { + data: [] + } + if (tasks.length > 0) { + list = (await Promise.all(tasks)).reduce((acc, cur) => { // reduce数组方法 累积拼接 + return { + data: acc.data.concat(cur.data) + } + }) + } + + // 获取第三方服务器端数据 + const playlist = await rp(URL).then((res) => { + return JSON.parse(res).result + }) + + // 数据库与服务器数据对比去重(数据已存在数据库的无需再重复添加) + const newData = [] + for(let i = 0, len1 = playlist.length; i < len1; i++) { + let flag = true + for(let j = 0, len2 = list.data.length; j < len2; j++) { + if(playlist[i].id === list.data[j].id){ + flag = false + break + } + } + if(flag){ + newData.push(playlist[i]) + } + } + + // 把数据插入数据库,需要单条插入 + for (let i = 0, len = newData.length; i < len; i++) { + await playlistCollection.add({ // 给数据库集合添加数据 + data: { + ...newData[i], + createTime: db.serverDate(), // db.serverDate() 获取服务器时间 + } + }).then((res) => { // 数据添加成功 + console.log('数据添加成功') + }).catch((err) => { // 失败 + console.error(err) + }) + } + return newData.length // 插入多少条数据 +} +``` + + + +##### 查询数据库 + +```js +//云函数中查询数据库的例子: + +// 云函数入口文件 +const cloud = require('wx-server-sdk') + +cloud.init() + +const TcbRouter = require('tcb-router') +const db = cloud.database() // 初始化数据库 +const blogCollection = db.collection('blog') // 博客的数据库集合 + +// 云函数入口函数 +exports.main = async (event, context) => { + const app = new TcbRouter({ event }) // 初始化TcbRouter + + app.router('list', async (ctx, next) => { + // skip 从第几条开始查,limit 查几条数据,orderBy(排序字段,排序方式) 排序,排序方式desc降序/asc升序 + ctx.body = await blogCollection.skip(event.start).limit(event.count) + .orderBy('createTime', 'desc').get().then((res) => { + return res.data + }) + + }) + + + return app.serve() // 必需返回 +} +``` + + + +##### 云函数调试 + +云控制台中可会云函数进行云端测试 + +在小程序调用云函数后,可查看云函数**日志** + + + + + +#### 定时触发云函数 + +如果云函数需要定时 / 定期执行,也就是定时触发,我们可以使用云函数定时触发器。配置了定时触发器的云函数,会在相应时间点被自动触发,函数的返回结果不会返回给调用方 + +云函数目录下新建 config.json + +[API](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/triggers.html) + +```json +{ + "triggers": [ + { + "name": "myTriggers", + "type": "timer", + "config":"0 0 10,14,16,20 * * * *" //表示每天的10点、14点、16点、20点触发一次 + } + ] +} +``` + +编辑好触发器之后,要在云函数目录 > 右键 > `上传触发器` + + + +#### 配置云函数超时时间 + +当云函数比较复杂的时候,默认的超时时间3秒可能不能够满足需求,可以适当的设置更为合理的时间 + +云开发控制台 > 云函数 > 配置 > 超时时间 + + + +#### 上拉加载与下拉刷新 + +```js +page页面json中: +"enablePullDownRefresh": true + + +page页面js中有这两个函数: + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh: function() { + this.setData({ + playlist: [] + }) + this._getPlaylist() + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom: function() { + this._getPlaylist() + }, + + + + 下拉刷新请求完数据后 + wx.stopPullDownRefresh() // 停止下拉刷新动画 +``` + + + +#### 云函数路由优化tcb-router + +一个用户在一个云环境只能创建50个云函数 + +假如小程序非常复杂,50个云函数不能够满足业务需求怎么办? + + + +相似的请求归类到同一个云函数处理 + + + +tcb-router是一个koa风格的云函数路由库 + + + +通俗理解就是可以把很多个接口归类到同一个云函数内。 + + + +github-tcb-router: + +koa洋葱模型... + + + +安装: + +```sh +在使用到tcb-router的云函数目录下打开命令行,输入命令进行安装 +npm install --save tcb-router +``` + + + +```js +// 云函数的 index.js +const TcbRouter = require('router'); // 必需 + +exports.main = (event, context) => { + const app = new TcbRouter({ event });// 必需 + + // app.use 表示该中间件会适用于所有的路由(全局中间件) 非必需 + app.use(async (ctx, next) => { // 这个中间件表示所有路由都会调用到,而路由中间件为单独调用 + ctx.data = {}; // 获取要传给小程序端的数据 + ctx.data.openId = event.userInfo.openId // 这里获取到的openId将分布到所有路由 + await next(); // 执行下一中间件 + }); + + // 路由为数组表示,该中间件适用于 user 和 timer 两个路由 + app.router(['user', 'timer'], async (ctx, next) => { + ctx.data.company = 'Tencent'; // 这里获取到的数据将分布到 user 和 timer 两个路由 + await next(); // 执行下一中间件 + }); + + // 路由为字符串,该中间件只适用于 user 路由 + app.router('user', async (ctx, next) => { + ctx.data.name = 'heyli'; // 获取要传给小程序端的数据 + await next(); // 执行下一中间件 + }, async (ctx, next) => { + ctx.data.sex = 'male'; // 获取要传给小程序端的数据 + await next(); // 执行下一中间件 + }, async (ctx) => { + ctx.data.city = 'Foshan'; // 获取要传给小程序端的数据 + // ctx.body 返回数据到小程序端 + ctx.body = { code: 0, data: ctx.data}; // 要传给小程序端的数据 + }); + + // 路由为字符串,该中间件只适用于 timer 路由 + app.router('timer', async (ctx, next) => { + ctx.data.name = 'flytam'; + await next(); // 执行下一中间件 + }, async (ctx, next) => { + ctx.data.sex = await new Promise(resolve => { + // 等待500ms,再执行下一中间件 + setTimeout(() => { + resolve('male'); + }, 500); + }); + await next(); // 执行下一中间件 + }, async (ctx)=> { + ctx.data.city = 'Taishan'; + + // ctx.body 返回数据到小程序端 + ctx.body = { code: 0, data: ctx.data }; + }); + + return app.serve(); // 必需 + +} + + +小程序端: + +// 调用名为 router 的云函数,路由名为 user +wx.cloud.callFunction({ + // 要调用的云函数名称 + name: "router", + // 传递给云函数的参数 + data: { + $url: "user", // 要调用的路由的路径,传入准确路径或者通配符* + other: "xxx" + } +}).then((res) => { + console.log(res) +}) +``` + + + +上面tcb-router代码会按照洋葱模型执行,即先从上往下逐个进入中间件,再从下往上逐个退出中间件。 + + + + + +#### 本地存储(缓存) + +```js +// 存储: +wx.setStorageSync(key, data) // 同步存储(存储成功再继续下一步操作) +wx.setStorage(key, data) // 异步存储(即使存储没成功也会执行下一步代码)、 + +// 读取: +wx.getStorageSync(key) // 同步 (读取到数据在进行下一步操作) +wx.setStorage(key) // 异步 + +``` + + + +#### api设置title + +```js +wx.setNavigationBarTitle({ + title: '', +}) +``` + + + +#### 背景播放音 + +[BackgroundAudioManager](https://developers.weixin.qq.com/miniprogram/dev/api/media/background-audio/BackgroundAudioManager.html) 全局唯一的背景音频管理器 + + + +```json +// 需要在app.json配置,才能使用后台音乐播放的能力 + +"requiredBackgroundModes": ["audio", "location"] +``` + + + +```js +// 获取全局唯一的背景音频管理器 +const backgroundAudioManager = wx.getBackgroundAudioManager() + + +backgroundAudioManager.src = 音频链接 +backgroundAudioManager.title = 音频标题 + +``` + + + + + +#### createSelectorQuery查询节点信息 + +[createSelectorQuery](https://developers.weixin.qq.com/miniprogram/dev/api/wxml/wx.createSelectorQuery.html) 小程序的方法,用于查询节点等操作 + +```js +const query = wx.createSelectorQuery() +query.select('#the-id').boundingClientRect() // 节点的布局信息 +query.selectViewport().scrollOffset() +query.exec(function(res){ + res[0].top // #the-id节点的上边界坐标 + res[1].scrollTop // 显示区域的竖直滚动位置 +}) +``` + + + +#### 组件内的方法 + +[Component(Object object)](https://developers.weixin.qq.com/miniprogram/dev/reference/api/Component.html) + + + +#### 组件生命周期 + +[lifetimes](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/lifetimes.html) + +```js +// 生命周期 +lifetimes: { + ready() { // 在组件在视图层布局完成后执行 + ... + } +}, +``` + +##### 组件所在页面的生命周期 + +```js +Component({ + pageLifetimes: { + show: function() { + // 页面被展示 + }, + hide: function() { + // 页面被隐藏 + }, + resize: function(size) { + // 页面尺寸变化 + } + } +}) +``` + + + +#### 组件对数据的监听 + +[observers](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/observer.html) + +```js +observers: { // 对数据的监听(数据初次加载完成也会执行) + 监听的数据对象(newData){ + console.log(newData) + } +}, +``` + + + +#### 子组件自定义事件传递给父组件 + +``` +子组件js: +// 触发自定义事件 向父组件传值, 参数x(可选,传递给父组件的参数,可以是对象或其他) +this.triggerEvent('自定义事件名', 参数x) + + +父组件wxml: +<子组件标签 bind:自定义事件名="执行的事件" /> + +父组件js: +执行的事件(event) { + console.log(event.detil.参数) +} +``` + + + +#### 父组件自定义事件传递给子组件 + +``` +父组件wxml: +<子组件标签 class="子组件类名"> + +父组件JS: +// 选择组件,并传入事件和参数 +this.selectComponent('.子组件类名').自定义事件名(传入参数) + +子组件js: +methods: { + 自定义事件名(参数x){ + console.log(参数x) + } +} + +``` + + + +#### 兄弟组件间传递事件和传值 + +``` +例子:子组件1向子组件2传递参数 + +父组件wxml中: +<子组件标签1 bind:自定义事件名1="执行的事件"> +<子组件标签2 class="子组件2类名"> + +父组件js: +执行的事件(event) { + this.selectComponent('.子组件2类名').自定义事件名2(event.detil.参数x) // 向子组件2传值 +} + +子组件1js: +// 触发自定义事件 向父组件传值, 参数x(可选,传递给父组件的参数,可以是对象或其他) +this.triggerEvent('自定义事件名1', 参数x) + + + +子组件2js: +methods: { + 自定义事件名2(参数x){ + console.log(参数x) // 接收父组件传入的值 + } +} + +``` + + + +#### 获取手机信息 + +[wx.getSystemInfo(Object object)](https://developers.weixin.qq.com/miniprogram/dev/api/base/system/system-info/wx.getSystemInfo.html) + +```js +wx.getSystemInfo({ + success(res){ + console.log(res) //手机信息 + } +}) +``` + + + + + +#### 滚动组件 + +[scroll-view](https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html) + +```html + + +``` + + + +#### 全局属性、方法(类似vuex) + +``` +在app.js中: + +onLaunch: function () { + this.globalData = {// 设置全局属性、方法 + test: 0 + } +}, +setGlobalData(dataItem, val) { // 设置全局属性 + this.globalData[dataItem] = val +}, +getGlobalData(dataItem) { // 获取全局属性 + return this.globalData[dataItem] +} + + +在需要调用的页面js中: +const app = getApp() // 在最顶部先调用app方法 + +// 设置全局属性 +app.setGlobalData('test', 1) + +// 获取全局属性 +app.getGlobalData('test') +``` + + + +#### 消息提示框 + +[showToast](https://developers.weixin.qq.com/miniprogram/dev/api/ui/interaction/wx.showToast.html) + +```js +wx.showToast({ + title: '成功', + icon: 'success', //图标: success 成功、loading 加载中、none 无 + duration: 2000 +}) +``` + + + + + + + +## 《发现》页面 + + + +#### 调用组件外部的样式 + +components内部的组件无法直接调用外部的样式。可通过以下方式调用组件外部样式: + +**方法一:** + +``` +父组件wxml: + + + + +子组件js: +// 组件外部样式 + externalClasses: [ + 'iconfont', // 对应的是上面等号前面的名称 + 'icon-sousuo' + ], + + 子组件wxml: 即可实现调用组件外的样式 + + + + 注意:如果想在组件内部再次修改样式,不能够引用外部传进来的class名称进行修改,可以另起一个class名称进行修改。 +``` + + + +**方法二:** + +[消除样式隔离](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html#组件样式隔离) + +```js +组件内: +Component({ + options: { + styleIsolation: 'apply-shared' + } +}) +``` + + + +#### 组件插槽slot + +**单个插槽** + +``` +父组件调用传入插槽内容: +<组件标签> + + 插槽内容 + 插槽内容 + + + + 组件内部定义slot标签: + + + + +``` + + + +**如果需要实现多个插槽** + +``` + +父组件调用传入插槽内容: +<组件标签> + + 插槽1内容 + 插槽1内容 + + + + 插槽2内容 + 插槽2内容 + + + +组件js : +options: {// 设置 + multipleSlots: true // 打开多个插槽功能 +}, + + +组件内部定义slot标签: + + + + + +``` + + + + + +#### 判断用户授权 + +[授权](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/authorize.html) + +```js +// 判断用户是否授权 + wx.getSetting({ + success: (res) => { // 这里使用箭头函数可改变内部this指向为外部的this + console.log(res) + if (res.authSetting['scope.userInfo']) { // 已授权 + wx.getUserInfo({ // 获取用户信息 + success(res) { + console.log(res) + } + }) + } else { // 未授权 + + } + } + }) +``` + + + + + +#### button的开发能力(获取用户信息)1 + +```js + + + +bindgetuserinfo 事件会询问用户是否同意授权 + + +js中: + onGetUserInfo(event) { // 获取用户信息 + const userInfo = event.detail.userInfo + if (userInfo) { // 用户允许授权 + this.setData({ + modalShow: false + }) + this.triggerEvent('loginSuccess', userInfo) // 给父组件传用户数据 + } else { // 用户拒绝授权 + this.triggerEvent('loginFail') + } + } +``` + + + +#### 原生组件 + +[原生组件](https://developers.weixin.qq.com/miniprogram/dev/component/native-component.html) + +```html + +auto-focus 自动获取焦点 + + +``` + + + + + + + + + +#### 选择上传图片 + +[上传图片](https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.chooseImage.html) + +```js +let max = 9 - this.data.images.length // 还能再选几张图片 +wx.chooseImage({ + count: max, // 还能再选几张图片 + sizeType: ['original', 'compressed'], // 初始值 and 压缩过的 + sourceType: ['album', 'camera'], // 手机相册选择 and 拍照选择 + success: (res) => { // 箭头函数改变this指向 + console.log(res) + }, + }) +``` + + + +#### 图片裁剪 + +[图片裁剪](https://developers.weixin.qq.com/miniprogram/dev/component/image.html) + +```html + + +``` + + + +#### 获取标签自定义属性data-* (删除图片的实现) + +``` + + + + + + + + + + + // 删除图片 + onDelImage(event) { + // event.target.dataset.index 获取标签属性data-index的值 + this.data.images.splice(event.target.dataset.index, 1) // splice会改变原有数组 + this.setData({ + images: this.data.images + }) + }, + +``` + + + + + + + +#### 全屏预览图片(点击图片放大预览) + +[全屏预览图片](https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.previewImage.html) + +```js +// 全屏预览图片 + onPreviewImage(event) { + wx.previewImage({ + urls: this.data.images, // 图片地址列表 + current: event.target.dataset.imgsrc // 当前预览图片地址 + }) + }, +``` + + + +#### 文件上传云存储(发布博客例子) + +[文件上传云存储](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-client-api/storage/uploadFile.html) + +```js + // 结合'发布'的例子: + send() { + // 验证是否输入内容 + if (content.trim() === '') { // trim() 去掉字符串空格 + wx.showToast({ + title: '请输入内容', + icon: 'none' + }) + return + } + wx.showLoading({ + title: '发布中', + }) + /** + * 实现思路及步骤: + * 1、图片 -> 上传 云存储 -> 生成 图片fineID(云文件ID) + * 2、数据 -> 录入 云数据库 + * 数据包括:文字内容、图片fineID、昵称、头像、发布时间、openId(用户唯一标识,在插入数据库是系统会自动添加_openId字段,不需要另外插入) + */ + let promiseArr = [] + let fileIds = [] + // 图片上传云存储 + this.data.images.forEach((item) => { + let p = new Promise((resolve, reject) => { + let suffix = /\.\w+$/.exec(item)[0] // 文件扩展名(文件后缀) + wx.cloud.uploadFile({ // 每次只能上传一个文件 + /** + * cloudPath 云路径。如果路径相同,后上传的文件会覆盖原文件 + * 路径:blog/云存储中的文件夹 + Date.now()时间戳 + Math.random()*1000000随机数 + 文件后缀 + */ + cloudPath: 'blog/' + Date.now() + '-' + Math.random() * 1000000 + suffix, + filePath: item, // 文件本地临时路径 + success: (res) => { + fileIds.push(res.fileID) + resolve() + }, + fail: (err) => { + console.error(err) + reject() + } + }) + }) + promiseArr.push(p) + }) + + // 存入云数据库 + Promise.all(promiseArr).then((res) => { + db.collection('blog').add({ + data: { + ...userInfo, // 昵称、头像 + content, // 内容 + img: fileIds, // 图片fileID列表 + createTime: db.serverDate() // 创建时间,取服务端时间 + } + }).then((res) => { + wx.hideLoading() + wx.showToast({ + title: '发布成功', + }) + // 返回博客页面,并刷新 + wx.navigateBack() + + }) + }).catch((err) => { + wx.hideLoading() + wx.showToast({ + title: '抱歉,发布失败', + icon: 'none' + }) + }) + }, +``` + + + +#### js模块化 (时间格式化) + + + +在目录utils 中新建formatTime.js文件 + +```js +// 时间格式化 模块封装 +module.exports = (date) => { // date 数据格式为 date + let fmt = 'yyyy-MM-dd hh:mm:ss' // 预定格式 + const o = { + // + 正则中的1个或多个 + 'M+': date.getMonth() + 1, + 'd+': date.getDate(), + 'h+': date.getHours(), + 'm+': date.getMinutes(), + 's+': date.getSeconds() + } + + if (/(y+)/.test(fmt)) { + // $1 表示正则中的第一个,即(y+) + fmt = fmt.replace(RegExp.$1, date.getFullYear()) // replace 替换 + } + + for (let k in o) { + if (new RegExp('('+ k +')').test(fmt)) { + fmt = fmt.replace(RegExp.$1, o[k].toString().length === 1 ? '0' + o[k] : o[k]) + } + } + + return fmt +} +``` + + + +在组件引入js模块 + +```js +import formatTime from '../../utils/formatTime.js' + +使用: +formatTime(new Date('Wed Aug 28 2019 16:23:06 GMT+0800 (中国标准时间)')) +``` + + + + + +#### 阻止事件冒泡 + +`bind` 和 `catch` 都可以绑定事件,它们的区别是 `bind` 有事件冒泡,而 `catch` 没有 + + + +#### 返回上一个页面并执行方法 + +[API](https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.navigateBack.html) + +```js + // 返回博客页面,并刷新 + wx.navigateBack() + const pages = getCurrentPages() // 获取当前页面栈 + const prevPage = pages[pages.length - 2] // 取到上一个页面 + prevPage.onPullDownRefresh() // 执行上一个页面的方法 onPullDownRefresh +``` + + + +#### 图片懒加载 + +[API](https://developers.weixin.qq.com/miniprogram/dev/component/image.html) + +```html +给image标签设置 lazy-load 为 true + + +.img { + background: #eee; +} +``` + +**懒加载占位图可以给image设置背景图或背景色** + + + +#### 模糊查询 + +```js +// 获取博客列表 + app.router('blogList', async (ctx, next) => { + const keyword = event.keyword // 搜索关键字 调用接口时传递来的数据 + let w = {} + if (keyword.trim() != '') { + w = { + content: db.RegExp({ // 正则 + regexp: keyword, + options: 'i' // i表示忽略大小写 + }) + } + } + + // where查询条件 skip 从第几条开始查,limit 查几条数据,orderBy(排序字段,排序方式) 排序,排序方式desc降序/asc升序 + ctx.body = await blogCollection.where(w).skip(event.start).limit(event.count) + .orderBy('createTime', 'desc').get().then((res) => { + return res.data + }) + + }) +``` + + + +#### 提升模糊查询的效率 (添加索引,对数据量大的查询效果明显) + +云开发控制台 > 数据库相应的集合 > 索引管理 > 添加索引 > 输入自定义索引名称、该字段的值是否唯一、被查询的字段名、升序/降序 > ok + + + + + +#### 小程序端调用云数据库 + +一般调用云数据库的操作都写在云函数内,其实小程序端也可以对数据库进行操作。 + +小程序端一次最多只能查询20条数据,云函数端最多可查询100条数据,可使用多次查询拼接的方式突破限制。 + +```js +// 小程序端调用云数据库示例 + const db = wx.cloud.database() // 初始化数据库 + db.collection('blog').orderBy('createTime','deac').get().then((res) => { + console.log(res) + }) +``` + + + + + +#### 云数据库权限管理 + +**注意:云控制台和服务端(云函数)始终有所有数据读写权限,** + +**但权限的管理仅对小程序端发起的请求有效。** + + + +* 仅创建者可写,所有人可读 (适合于文章) + +* 仅创建者可读写 (适用于私密内容) + +* 仅管理端可写,所有人可读(适用于商品信息) + +* 仅管理端可读写(适用于后台敏感数据) + + + + + +#### 数据库中1对N关系的三种设计方式 + +##### 第一种:N的数量较少 几十个以内 + +**1 条记录存储 N 个子数据** + +​ 如一条博客中,最多有9张图片,这9张图片可和其他数据放在一个记录中。 + +```js +[ + { + id:... + img:[ + '...', '...', '...', '...', '...', '...', '...', '...', '...' + ] + } +] +``` + + + +##### 第二种:N的数量较多 几十到几百个 + +**1 存储 每个N的 id** + +可分两个数据库集合, + +一个为 '目录' 集合,存放 '详情' 集合下的每条数据的 id 目录 + +一个为 '详情' 集合,每条数据对应一个单独的 id 和 详细数据 + + + +```js +目录集合: +[ + { + 'id':"11", + 'name': '产品1', + 'xqs': ['111','222','333', ... ] // 存放 详情集合 中的每条数据 id + } +] + + + +详情集合: +[ +{'id':"111",name:'零件1',title:'...' ...}, +{'id':"222",name:'零件2',title:'...' ...}, +{'id':"333",name:'零件3',title:'...' ...}, +... +] + + +``` + +如歌单列表,与歌曲详情的数据组合设计。 + + + +##### 第三种:N的数量巨大 几百成千上万个 + +**每个 N 都存储 1 的 id** + +如新浪博客中的一条博客下面有几千条评论 + +```js +一条新浪博客: +[{ + 'id':'11', + 'content':'博客内容' + ... +}] + + +上千条评价: +[ +{ + 'id':'111111' + 'blogId':'11', // 这个id对应的是那一条博客的id + 'content': '评价内容1' +}, +{ + 'id':'222222' + 'blogId':'11', // 这个id对应的是那一条博客的id + 'content': '评价内容2' +}, +{ + 'id':'33333' + 'blogId':'11', // 这个id对应的是那一条博客的id + 'content': '评价内容3' +}, +... +] + +``` + + + +#### 云调用 + +通过云函数调用服务端的开发接口 + +这些接口如:模板消息推送、生成小程序码... + + + + + +#### 模板消息推送 + + + +**1、使用from表单才能触发消息推送,并设置report-submit="true"** + +```html + +
+ + +
+``` + +**2、需要到微信公众平台做相应的设置:** + +微信公众平台 > 功能 > 模板消息 > 添加模板 > 选择相应的模板> 添加成功后会有一个模板ID + + + +**3、新建一个云函数,用于云调用。在该云函数下新建配置文件:config.json ,用于配置权限** + +config.json : + +```json +{ + "permissions": { + "openapi": [ + "templateMessage.send" + ] + } +} +``` + +云函数设置消息推送: + +```js +// 云函数入口函数 +exports.main = async (event, context) => { + // 获取openid + const { OPENID } = cloud.getWXContext() + + // 模板推送消息 + const result = await cloud.openapi.templateMessage.send({ + touser: OPENID, + page: `/pages/blog-comment/blog-comment?blogId=${event.blogId}`, // 用户点击推送消息打开的页面 + data: { // 模板的内容,keyword为在公众平台设置模板时对应的字段 + keyword1: { // 评价内容 + value: event.context + }, + keyword2: { // 评价时间 + value: event.time + } + }, + templateId: 'LNwKMcYwlz-0HabgBhmZi6CWZrlNSBiNJ2h0SMorcxQ', // 模板id,到公众平台模板消息上获取 + formId: event.formId // 触发消息推送的form表单的id + }) + + return result +} +``` + + + +**4、在提交表单事件完成后调用消息推送云函数** + +```js + wx.cloud.callFunction({ + name: 'sendMessage', + data: { + content, + formId, + blogId: this.properties.blogId + } + }).then((res) => { + console.log(res) + }) +``` + + + + + +#### 云函数多集合查询数据库 + +```js +// 博客详情(博客内容、评论) + app.router('blogDetail', async(ctx, next) => { + let blogId = event.blogId + + // 博客内容 + let detail = await blogCollection.where({ + _id: blogId + }).get().then((res) => { + return res.data + }) + + // 评论查询 + const countResult = await blogCollection.count() + const total = countResult.total + let commentList = { + data: [] + } + if (total > 0) { + // 突破100条限制 + const batchTimes = Math.ceil(total / MAX_LIMIT) + const tasks = [] + for (let i = 0; i < batchTimes; i++) { + let promise = db.collection('blog-comment').skip(i * MAX_LIMIT) + .limit(MAX_LIMIT).where({ + blogId + }).orderBy('createTime', 'desc').get() + tasks.push(promise) + } + if (tasks.length > 0) { + commentList = (await Promise.all(tasks)).reduce((acc, cur) => { + return { + data: acc.data.concat(cur.data) + } + }) + } + + } + ctx.body = { + detail, + commentList + } + }) +``` + + + +#### 分享功能 + +分享功能需要button标签,设置open-type="share" + +```html + + +``` + +在js中有onShareAppMessage方法,点击button会自动执行此方法 + +```js +onShareAppMessage: function (event) { + console.log(event) + + // 对分享卡片的设置 + let blogObj = event.target.dataset.blog + return { + title: blogObj.content, + path: `/pages/blog-comment/blog-comment?blogId=${blogObj._id}`, + // imageUrl: '' // 自定义图片,不支持云存储的图片 + } + } +``` + + + +#### 不同场景获取用户信息的方式 + +##### 场景一:只想在界面上显示自己的昵称和头像 + +以组件的方式:根据type类型获取不同用户数据 + +**该方式不需要授权,只能用于在wxml显示自己的信息** + +[open-data](https://developers.weixin.qq.com/miniprogram/dev/component/open-data.html) + +```html + + +... +``` + + + +##### 场景二:在JS中获取用户信息 + +该方式要在用户授权以后才能获取用户信息 + +[wx.getUserInfo](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserInfo.html) + +```js +wx.getUserInfo({ + success: (res) => { + console.log(res) + } + }) +``` + +在未授权的情况下需要用户先授权: + +```js +// 判断用户是否授权 + wx.getSetting({ + success: (res) => { // 这里使用箭头函数可改变内部this指向为外部的this + if (res.authSetting['scope.userInfo']) { // 已授权 + wx.getUserInfo({ // 获取用户信息 + success: (res) => { // 这里使用箭头函数可改变内部this指向为外部的this + + app.setGlobalData('userInfo', res.userInfo) // 设置app全局属性 + + this.onLoginSuccess({ + detail: res.userInfo + }) + } + }) + } else { // 未授权 + this.setData({ // 打开弹出层,显示获取用户信息按钮 + modalShow: true + }) + } + } + }) + + + 授权按钮 + + + + onGetUserInfo(event) { // 获取用户信息 + const userInfo = event.detail.userInfo + if (userInfo) { // 用户允许授权 + this.setData({ + modalShow: false + }) + this.triggerEvent('loginSuccess', userInfo) // 给父组件传用户数据 + } else { // 用户拒绝授权 + this.triggerEvent('loginFail') + } + } + +``` + +> 注意:上面这种方式没有获取到openId + + + + + +##### 场景三:获取openId + + + +**获取openId不需要用户授权** + +1、传统开发方式获取openId,后台服务器由自己开发,没使用云开发 + + + +小程序端 微信服务器 后端服务器 + +步骤: + +小程序端 调用 wx.login 向微信服务器 获取code + +小程序端 调用 wx.request 将 code 传递给 后端服务器 + +后端服务器 使用code 向微信服务器 换取openid和session_key + +后端服务器 将openid 发送给 小程序端 + + + +2、云开发方式获取openId + +云函数login中 + +```js +// 获取 WX Context (微信调用上下文),包括 OPENID、APPID、及 UNIONID(需满足 UNIONID 获取条件) + const wxContext = cloud.getWXContext() + + return { + event, + openid: wxContext.OPENID, + appid: wxContext.APPID, + unionid: wxContext.UNIONID, + } +``` + + + + + +```js +普通按钮 + + +getOpenid() { + wx.cloud.callFunction({ + name: 'login' + }).then((res) => { + console.log(res) + }) +} +``` + + + +>openid 在小程序和公众号下是不一样的 +> +>unionid 在小程序和公众号下都是一样的 + + + + + +## 《我的》页面 + + + +json文件 + +```js +"navigationBarTitleText": "我的", + "disableScroll": true // 使页面无法滚动 +``` + + + + + +#### 导航页面链接跳转 + +[navigator](https://developers.weixin.qq.com/miniprogram/dev/component/navigator.html) + + + +#### 背景图片 + +wxss背景图片不支持本地相对路径的图片,只支持网络图片和base64图片 + +建议使用base64图片,图片文件最好不要太大。 + + + +#### 每个页面都有的page标签 + +```js +page { + background-color: #f1f1f1; +} +``` + + + +#### 播放历史与本地存储 + +方案一:播放历史存储在数据库当中,这样在不同设备访问都可查看播放历史。读取速度相对较慢 + +方案二:播放历史存储在本地,仅当前设备可查看播放历史。读取速度较快 + + + +本项目采用本地存储: + +使用openid作为本地存储的key,播放历史存入value + + + +在app.js中获取openid,即打开小程序就获取openid。 + +```js +// app.js +onLaunch: function () { + this.getOpenid() // 获取openid并存储 +}, +getOpenid() { // 获取openid并存储 + wx.cloud.callFunction({ + name: 'login' + }).then((res) => { + const openid = res.result.openid + this.globalData.openid = openid // 保存到全局变量 + if (wx.getStorageSync(openid) == '') { // 该用户从未打开过小程序,未存储过openid在本地 + wx.setStorageSync(openid, []) // 存储openid到本地 + } + }) + } +``` + + + +歌曲播放时 + +```js +// 保存播放历史到本地存储 + savePlayHistory() { + const currentSong = musiclist[nowPlayingIndex] // 当前播放歌曲 + const openid = app.globalData.openid // 从全局属性获取openid + const playHistory = wx.getStorageSync(openid) // 从本地存储获取播放历史数组 + + for (let i = 0, len = playHistory.length; i < len; i++) { + if (playHistory[i].id === currentSong.id) { // 当前播放歌曲已存在播放历史中 + playHistory.splice(i, 1) // 删除原纪录 + break + } + } + + playHistory.unshift(currentSong) // 在数组开头插入 + wx.setStorage({ // 存入本地 + key: openid, + data: playHistory + }) + + }, +``` + + + +播放历史页面获取 + +```js +onLoad: function (options) { + + const openid = app.globalData.openid //从全局属性获取openid + const playHistory = wx.getStorageSync(openid) // 读取本地播放历史数据 + + if (playHistory.length !== 0) { // 有播放历史 + this.setData({ + playHistory + }) + wx.setStorage({ // storage里把musiclist(播放列表)的内容换成播放历史的列表 + key: 'musiclist', + data: playHistory, + }) + } + + }, +``` + + + +#### 我的发现 + +代码分别演示了从云函数和小程序端获取数据,从小程序端获取数据享有**权限管理**的能力,不需要传openid。 + + + + + +#### 小程序码 + +[获取小程序码](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/qr-code.html) + +本项目演示使用[接口 B:适用于需要的码数量极多的业务场景](https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html) `云调用` 的方式。 + +步骤: + +* 创建云函数 gteQRCode + +* gteQRCode云函数下创建config.json配置权限,代码如下: + + ```json + { + "permissions":{ + "openapi":[ + "wxacode.getUnlimited" + ] + } + } + ``` + + + +```js +// 云函数入口函数 +exports.main = async (event, context) => { + const wxContext = cloud.getWXContext() + + const result = await cloud.openapi.wxacode.getUnlimited({ + scene: wxContext.OPENID, // 链接参数 不一定传openid,可传其他任意数据,然后通过此数据,在别人扫码进入时就可用于判断 + // page: "pages/blog/blog" // 默认进入主页 + // lineColor: { // 线条颜色 + // 'r': 211, + // 'g': 60, + // 'b': 57 + // }, + // isHyaline: true // 是否透明 + }) + + // result为二进制数据, 先上传到云存储 + + // 上传云存储 + const upload = await cloud.uploadFile({ + cloudPath: 'qrcode/qrcode' + Date.now() + Math.random() + '.png', + fileContent: result.buffer + }) + + return upload.fileID +} +``` + + + +##### 判断是从扫码小程序码进入,以及参数获取 + +```js +// 在从小程序码进入的页面js,onLoad方法中, + +onLoad: function (options) { + console.log(options.scene) // 获取到小程序码进入的参数 +} +``` + + + + + +### 版本更新检测 + +```js +// app.js +onLaunch: function(options) { + this.checkUpate() +}, +checkUpate(){ + const updateManager = wx.getUpdateManager() + // 检测版本更新 + updateManager.onCheckForUpdate((res)=>{ + if (res.hasUpdate){ + updateManager.onUpdateReady(()=>{ + wx.showModal({ + title: '更新提示', + content: '新版本已经准备好,是否重启应用', + success(res){ + if(res.confirm){ + updateManager.applyUpdate() + } + } + }) + }) + } + }) + }, +``` + + + +### 性能优化 + +[官网文档优化建议](https://developers.weixin.qq.com/miniprogram/dev/framework/performance/tips.html) + +使用开发者工具的调试器,Audits进行评分,然后根据提示针对项目进行优化。 + + + +### 场景值scene的作用与应用场景 + +[场景值](https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/scene.html) + +场景值用来描述用户进入小程序的路径。完整场景值的含义请查看[场景值列表](https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html)。 + + + +可根据不同场景进入实现不同业务处理,比如一个点餐小程序,店家内贴了小程序码,用户通过扫码进入,可立即进入点餐页面,等等 + + + +在app.js中的onLaunch(options) 、onShow(options),options包含scene场景值 + + + +**开发者工具中,切后台,可模拟进入场景。** + + + +### 小程序的"SEO"---页面收录sitemap + +在app.js的同级目录下有sitemap.json文件,用于配置收录规则 + + + +[stiemap配置](https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html) + + + +作用: + +使小程序搜索可根据小程序的内容进行搜索到 + + + +使用方法: + +1、在微信公众平台,小程序信息 > 页面收录设置 > 打开 (默认是已开启) + +2、打开sitemap.json文件,配置收录规则 + + + +``` json +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ // 收录规则 + "action": "allow",// 是否被收录,allow允许被收录,disallow不允许 + "page": "*" // *星号表示所有页面都被收录 + }] +} +``` + + + +```json +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ // 收录规则,可添加多条 + "action": "allow", // 是否被收录 + "page":"pages/player/player", // 页面 + "params": ["musicId","index"], // 链接的动态参数 + "matching":'exact' // 表示params的参数是否要准确的匹配 + },{ + "action": "disallow", // 是否被收录 + "page":"*", / + }] +} + +// 上面配置规则表示除了player页面被收录外,其他页面不被收录 +``` + + + + + +### 小程序上线审核流程 + + + +微信公众平台,版本管理>把小程序上传为体验版》提交审核》上线 + + + + + +## 后台管理系统 + +#### 架构示意图 + +​ 前端 后台 小程序云开发 + +vue-admin-template <---通过ajax--> 基于Koa2;HTTP API 或 tcb-admin-node ---->云函数、云数据库、云存储 + +​ + + + +#### vue-admin-template构建管理系统前端 + + + +[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) 基于element的后台管理系统模板 + +[vue-admin-template](https://github.com/PanJiaChen/vue-admin-template) 是 [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)的简化版 + + + +使用方法查看官方文档。 + + + + + +#### Koa2构建管理系统后端 + +官网: + + + +新建空文件夹wx-music-admin-backend,打开终端: + +```sh +# 生成package.json文件,-y 表示默认的配置 +npm init -y + +# 安装koa +npm install koa + +# 新建app.js文件 (win10系统命令),作为项目入口文件 +type nul > app.js + +``` + + + +app.js: + +```js +const Koa = require('koa') +const chalk = require('chalk') // 使console.log打印文字有颜色的插件,需: npm i chalk +const app = new Koa() + +app.use(async (ctx) => { + ctx.body = 'Hello Wolrd' +}) +const port = 3000 +app.listen(port, () => { // 端口号,开启服务后的回调函数 + console.log(chalk.green(`> 服务已开启,访问:http://localhost:${port}`)) +}) +``` + + + +终端: + +```sh +# node启动项目 +node app.js + +# 访问:http://localhost:3000 +``` + + + +#### 接口调用凭证 access_token 的缓存与更新 + + + +access_token,微信的接口调用凭证,详情: + + + +回到项目wx-music-admin-backend,打开终端: + +```sh +# HTTP 请求 插件 +npm i request +npm i request-promise +``` + +```js +/** + * 获取微信接口调用凭证 + * 详情:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html + */ + + +const rp = require('request-promise') // node发送http请求的插件 +const fs = require('fs') // node文件模块 +const path = require('path') // node 路径模块 + +//fileName = __dirname 当前文件所在目录的绝对路径, 加上 './access_token.json' +const fileName = path.resolve(__dirname, './access_token.json') + +// 这两个参数的获取:微信公众平台>开发>开发设置 +const APPID = 'wxc4e0b2d98063b103' +const APPSECRET = 'xxx' //小程序密钥,注意保密! + +// 微信 access_token 请求地址 +const URL = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}` + +// 发送请求获取AccessToken +const updateAccessToken = async () => { + const resStr = await rp(URL) + const res = JSON.parse(resStr) + + if (res.access_token) { + // node写文件,参数:1 文件路径,2 文件内容, 首次写文件为新建,往后为覆盖 + fs.writeFileSync(fileName, JSON.stringify({ + access_token: res.access_token, + createTime: new Date() + })) + } else { // 如获取不到,再次获取 + await updateAccessToken() + } +} + +// 读取access_token +const getAccessToken = async () => { + try { + // node读取文件,参数:1 读取的文件,2 字符集 + const readRes = fs.readFileSync(fileName, 'utf8') + const readObj = JSON.parse(readRes) + + // 如果服务器宕机导致setInterval无法定时更新,这里需要再次判断access_token的有效性 + const createTime = new Date(readObj.createTime).getTime() + const nowTime = new Date().getTime() + if((nowTime - createTime) / 1000 / 60 / 60 >= 2) { + await updateAccessToken() + await getAccessToken() + return + } + return readObj.access_token + + } catch (error) { //捕获异常,在未创建文件时,先创建文件 + await updateAccessToken() + await getAccessToken() + } +} + +// access_token有效期为2个小时,定时更新 +setInterval(async () => { + await updateAccessToken() +}, (7200 - 300) * 1000) + +module.exports = getAccessToken +``` + + + +#### 后端代码通过HTTP API 触发云函数获取数据 + +[HTTP API 触发云函数](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/functions/invokeCloudFunction.html) + + + + + + + +#### 产生跨域和后端解决跨域问题 + +管理系统前端向管理系统后端请求数据,产生了跨域问题 + +```js + // 产生跨域的几种情况 + // http://www.a.com https://www.a.com 协议不同 + // http://www.a.com http://www.b.com 域名不同 + // http://www.a.com http://news.a.com 主域与子域不同 + // http://www.a.com:8080 http://www.a.com:3000 端口不同 + +// 解决跨域的几种方法 +// jsonp +// iframe +// postMessage跨域 +// 跨域资源共享(CORS) +``` + + + +管理系统后端,安装 + +```sh +// 解决跨域问题的koa包 +npm i koa2-cors +``` + + + +app.js + +```js +//处理跨域 +app.use(cors({ + origin: ['http://localhost:9528'], // 允许访问本服务的域 + credentials: true +})) +``` + + + +#### 云数据库的增删改查接口 + +[数据库查询记录](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/database/databaseQuery.html) + + + +#### 后端获取前端post请求传来的数据 + +get请求可以直接通过ctx.request.query获取,但是post请求需要安装koa-body + +```sh +npm i koa-body +``` + + + +app.js + +```js +const koaBody = require('koa-body') // 对post请求前端传来的数据的获取,需要此依赖 + +// 接收post参数解析 +app.use(koaBody({ + multipart: true +})) + +``` + + + + + +接口.js + +```js +router.post('/updatePlaylist', async (ctx, next) => { + + const params = ctx.request.body // post请求获取前端传来的数据,需安装和配置koa-body + +}) +``` + + + +#### 后端获取云存储图片 + +云存储中上传图片,云数据库中新建图片的集合,并添加数据字段,字段包含云文件的fileid。 + +后端项目通过调用云数据库的方式获取数据 + +```js +router.get('/list', async (ctx, next) => { + // 接口中读取数据库默认最多10条数据 + const query = `db.collection('swiper').get()` + const res = await callCloudDB(ctx, 'databasequery', query) + console.log(res) + +}) +``` + +但获取到的数据为fileid,并不能用于显示图片,需要通过微信HTTP API获取云存储的接口来获取图片地址 + +[获取云存储](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/batchDownloadFile.html) + + + +#### 后端上传图片到云存储 + +[文件上传](https://developers.weixin.qq.com/miniprogram/dev/wxcloud/reference-http-api/storage/uploadFile.html) + + + + + + + + + + + diff --git a/docs/other/yaml.md b/docs/other/yaml.md new file mode 100644 index 0000000..cd4108c --- /dev/null +++ b/docs/other/yaml.md @@ -0,0 +1,340 @@ +# yaml教程 + +## 简介 + +YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。 + +YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别**适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲**(例如:许多电子邮件标题格式和YAML非常接近)。 + +YAML 的配置文件后缀为 **.yml**,如:**runoob.yml** 。 + + + +> yaml常用来用作配置文件,和json类似 + +## 基本语法 + +- 大小写敏感 + +- 使用缩进表示层级关系 + +- 缩进不允许使用tab,只允许空格 + +- 缩进的空格数不重要,只要相同层级的元素左对齐即可 + +- '#'表示注释 + + + +## 数据类型 + +YAML 支持以下几种数据类型: + +- **对象**:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary) +- **数组**:一组按次序排列的值,又称为序列(sequence) / 列表(list) +- **纯量**(scalars):单个的、不可再分的值 + + + +## YAML 对象 + +对象的几种表示形式: + +1、对象键值对使用冒号结构表示 **key: value**,冒号后面要加一个空格。 + +```yaml +key: value +``` + + + +2、也可以使用 **key:{key1: value1, key2: value2, ...}**。 + +```yaml +key:{child-key1:value1,child-key2:value2} +``` + + + +3、还可以使用缩进表示层级关系; + +```yaml +key: + child-key1: value1 + child-key2: value2 +``` + +同2,只是表示形式不同。 + + + +4、较为复杂的对象格式,可以使用问号加一个空格代表一个复杂的 key,配合一个冒号加一个空格代表一个 value: + +```yaml +? + - complexkey1 + - complexkey2 +: + - complexvalue1 + - complexvalue2 +``` + +意思即对象的属性是一个数组 [complexkey1,complexkey2],对应的值也是一个数组 [complexvalue1,complexvalue2] + + + +## YAML 数组 + +以 **-** 开头的行表示构成一个数组: + +```yaml +- A +- B +- C +``` + +YAML 支持多维数组,可以使用行内表示: + +```yaml +key: [value1, value2, ...] +``` + +数据结构的子成员是一个数组,则可以在该项下面缩进一个空格。 + +```yaml +- + - A + - B + - C +``` + +一个相对复杂的例子: + +```yaml +companies: + - + id: 1 + name: company1 + price: 200W + - + id: 2 + name: company2 + price: 500W +``` + +意思是 companies 属性是一个数组,每一个数组元素又是由 id、name、price 三个属性构成。 + +数组也可以使用流式(flow)的方式表示: + +```yaml +companies: [{id: 1,name: company1,price: 200W},{id: 2,name: company2,price: 500W}] +``` + + + +## 复合结构(对象和数组组合) + +数组和对象可以构成复合结构,例: + +```yaml +languages: + - Ruby + - Perl + - Python +websites: + YAML: yaml.org + Ruby: ruby-lang.org + Python: python.org + Perl: use.perl.org +``` + +转换为 js 为: + +```yaml +{ + languages: [ 'Ruby', 'Perl', 'Python'], + websites: { + YAML: 'yaml.org', + Ruby: 'ruby-lang.org', + Python: 'python.org', + Perl: 'use.perl.org' + } +} +``` + + + +## 纯量 + +纯量是最基本的,不可再分的值,包括: + +- 字符串 +- 布尔值 +- 整数 +- 浮点数 +- Null +- 时间 +- 日期 + +使用一个例子来快速了解纯量的基本使用: + +```yaml +boolean: # 布尔值 + - TRUE #true,True都可以 + - FALSE #false,False都可以 +float: # 浮点数 + - 3.14 + - 6.8523015e+5 #可以使用科学计数法 +int: # 整数 + - 123 + - 0b1010_0111_0100_1010_1110 #二进制表示 +null: # Null + nodeName: 'node' + parent: ~ #使用~表示null +string: # 字符串 + - 哈哈 # 字符串默认不使用引号表示 + - 'Hello world' #可以使用双引号或者单引号包裹特殊字符 + - newline + newline2 #字符串可以拆成多行,每一行会被转化成一个空格 +date: + - 2018-02-17 #日期必须使用ISO 8601格式,即yyyy-MM-dd +datetime: + - 2018-02-17T15:02:31+08:00 #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区 +``` + + + + 多行字符串可以使用`|`保留换行符,也可以使用`>`折叠换行。 + +```yaml +this: | + Foo + Bar +that: > + Foo + Bar +``` + + 转为js代码如下。 + +```json +{ this: 'Foo\nBar\n', that: 'Foo Bar\n' } +``` + + `+`表示保留文字块末尾的换行,`-`表示删除字符串末尾的换行。 + +```yaml +s1: | + Foo + +s2: |+ + Foo + + +s3: |- + Foo +``` + + 转为json代码如下 + +```json +{ s1: 'Foo\n', s2: 'Foo\n\n\n', s3: 'Foo' } +``` + + + + 字符串之中可以插入 HTML 标记。 + +```yaml +message: | +

+ 段落 +

+``` + + + + + + + +## 转换数据格式 + + 允许使用两个感叹号,强制转换数据类型。 + +```yaml +e: !!str 123 +f: !!str true +``` + +转换js结果同: + +```json +{ e: '123', f: 'true' } +``` + + + + + +## 锚点引用 + +**&** 锚点和< span class="marked">* 别名,可以用来引用: + +```yaml +defaults: &defaults # 添加锚点 + adapter: postgres + host: localhost + +development: + database: myapp_development + <<: *defaults # <<表示合并,*引用锚点 + +test: + database: myapp_test + <<: *defaults # <<表示合并,*引用锚点 +``` + +相当于: + +```yaml +defaults: + adapter: postgres + host: localhost + +development: + database: myapp_development + adapter: postgres + host: localhost + +test: + database: myapp_test + adapter: postgres + host: localhost +``` + +**&** 用来建立锚点(defaults),**<<** 表示合并到当前数据,***** 用来引用锚点。 + +下面是另一个例子: + +```yaml +- &showell Steve +- Clark +- Brian +- Oren +- *showell +``` + +转为js代码如下: + +```yaml +[ 'Steve', 'Clark', 'Brian', 'Oren', 'Steve' ] +``` + + + +> 参考: +> +> - yaml官网: +> - yaml与js的转换demo: +> - 菜鸟教程: \ No newline at end of file diff --git a/docs/python/README.md b/docs/python/README.md deleted file mode 100644 index 9e86a96..0000000 --- a/docs/python/README.md +++ /dev/null @@ -1 +0,0 @@ -> 敬请期待 \ No newline at end of file diff --git a/docs/readme.md b/docs/readme.md index b6b972f..404ba2b 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -6,13 +6,13 @@ tagline: web前端工程师,热衷于学习与总结 features: - title: 前端 - details: web前端开发相关技术 + details: JavaScript、ES6、vue框架等相关技术 url: /web/JavaScript/01.JavaScript中的名词概念 imgname: /img/web.png - - title: python - details: 人生苦短,我学python - url: /python/ - imgname: /img/python.png + - title: 页面 + details: html(5)/css(3),前端页面相关技术 + url: /ui/00.flex布局语法 + imgname: /img/ui.png - title: 技术杂谈 details: 技术文档、教程、技巧、学习笔记等 url: /other/git @@ -28,19 +28,12 @@ footer: Copyright © 2019-present evanblog * [多种数组去重性能对比](web/JavaScript/06.多种数组去重性能对比) * [更多... 👉](web/JavaScript/01.JavaScript中的名词概念) - - -* CSS/HTML - * [flex布局基础](web/CSS-HTML/01.flex布局基础) - * [flex布局案例-圣杯布局](web/CSS-HTML/04.flex布局案例-圣杯布局) - * [CSS3之animation动画](web/CSS-HTML/07.CSS3之animation动画) - * [更多... 👉](web/CSS-HTML/01.flex布局基础) - - - -## 🐍python - -> 敬请期待 +## 🎨 页面 +* HTML/CSS + * [flex布局语法](ui/00.flex布局语法) + * [flex布局案例-圣杯布局](ui/04.flex布局案例-圣杯布局) + * [CSS3之animation动画](ui/07.CSS3之animation动画) + * [更多... 👉](ui/00.flex布局语法) @@ -50,17 +43,17 @@ footer: Copyright © 2019-present evanblog * [Git使用文档](other/git) * [GitHub高级搜索技巧](other/github) * [Markdown使用教程](other/markdown) + * [更多... 👉](other/git) * 学习 * [学习效果低,忘性很大怎么办?](other/LearningAndMemory) * [学习网站分享](other/study) + * [更多... 👉](other/LearningAndMemory) * 面试 * [面试问题集锦](other/interview) * 其他 * [在线工具](other/utils) * [关于本博客搭建](https://github.com/xugaoyi/evanblog) -* [更多... 👉](other/git) - ## :email: 联系 - **Github**: diff --git a/docs/ui/00.flex布局语法.md b/docs/ui/00.flex布局语法.md new file mode 100644 index 0000000..09301a1 --- /dev/null +++ b/docs/ui/00.flex布局语法.md @@ -0,0 +1,305 @@ +# flex布局语法 + +## 一、flex 布局是什么? + +Flex是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。 + + + +**任何一个容器** 都可以指定为 Flex 布局。 + +```css +.box{ + display: flex; +} +``` + + + +**行内元素** 也可以使用 Flex 布局。 + +```css +.box{ + display: inline-flex; +} +``` + + + +Webkit 内核的浏览器,必须加上`-webkit`前缀。 + +```css +.box{ + display: -webkit-flex; /* Safari */ + display: flex; +} +``` + + + +**注意**,设为 Flex 布局以后,子元素的`float`、`clear`和`vertical-align`属性将失效。 + + + +## 二、基本概念 + +采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。 + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071004.png) + +容器默认存在两根轴:水平的主轴(或x轴)(main axis)和垂直的交叉轴(或y轴)(cross axis)。主轴的开始位置(与边框的交叉点)叫做`main start`,结束位置叫做`main end`;交叉轴的开始位置叫做`cross start`,结束位置叫做`cross end`。 + +项目默认沿主轴排列。单个项目占据的主轴空间叫做`main size`,占据的交叉轴空间叫做`cross size`。 + + + +## 三、容器的属性 + +以下6个属性设置在容器上。(即设置在 display: flex; 的元素上) + + + +>- flex-direction 方向 决定项目的排列方向 +> +>- flex-wrap 换行 +> +>- flex-flow 方向和换行的简写 +> +> +> +>- justify-content 主轴对齐方式 +> +>- align-items 交叉轴对齐方式 +> +>- align-content 多轴线的对齐方式 + + + +### 3.1 flex-direction属性 + +`flex-direction`属性决定主轴的方向(即项目的排列方向)。 + +> ```css +> .box { +> flex-direction: row | row-reverse | column | column-reverse; +> } +> ``` + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071005.png) + +它可能有4个值。 + +> - `row`(默认值):主轴为水平方向,起点在左端。 +> - `row-reverse`:主轴为水平方向,起点在右端。 +> - `column`:主轴为垂直方向,起点在上沿。 +> - `column-reverse`:主轴为垂直方向,起点在下沿。 + +### 3.2 flex-wrap属性 + +默认情况下,项目都排在一条线(又称"轴线")上。`flex-wrap`属性定义,如果一条轴线排不下,如何换行。 + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071006.png) + +> ```css +> .box{ +> flex-wrap: nowrap | wrap | wrap-reverse; +> } +> ``` + +它可能取三个值。 + +(1)`nowrap`(默认):不换行。会压缩项目的宽度 + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071007.png) + +(2)`wrap`:换行,第一行在上方。 + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071008.jpg) + +(3)`wrap-reverse`:换行,第一行在下方。 + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071009.jpg) + +### 3.3 flex-flow + +`flex-flow`属性是`flex-direction`属性和`flex-wrap`属性的简写形式,默认值为`row nowrap`。 + +> ```css +> .box { +> flex-flow: || ; +> } +> ``` + +### 3.4 justify-content属性 + +`justify-content`属性定义了项目在主轴上的对齐方式。 + +> ```css +> .box { +> justify-content: flex-start | flex-end | center | space-between | space-around; +> } +> ``` + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071010.png) + +它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。 + +> - `flex-start`(默认值):左对齐 +> - `flex-end`:右对齐 +> - `center`: 居中 +> - `space-between`:两端对齐,项目之间的间隔都相等。 +> - `space-around`:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。 + +### 3.5 align-items属性 + +`align-items`属性定义项目在交叉轴上如何对齐。 + +> ```css +> .box { +> align-items: flex-start | flex-end | center | baseline | stretch; +> } +> ``` + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071011.png) + +它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。 + +> - `flex-start`:交叉轴的起点对齐。 +> - `flex-end`:交叉轴的终点对齐。 +> - `center`:交叉轴的中点对齐。 +> - `baseline`: 项目的第一行文字的基线对齐。 +> - `stretch`(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。 + +### 3.6 align-content属性 + +`align-content`属性定义了多根轴线的对齐方式(**一排项目为一根轴线**)。如果项目只有一根轴线,该属性不起作用。 + +> ```css +> .box { +> align-content: flex-start | flex-end | center | space-between | space-around | stretch; +> } +> ``` + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071012.png) + +该属性可能取6个值。 + +> - `flex-start`:与交叉轴的起点对齐。 +> - `flex-end`:与交叉轴的终点对齐。 +> - `center`:与交叉轴的中点对齐。 +> - `space-between`:与交叉轴两端对齐,轴线之间的间隔平均分布。 +> - `space-around`:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。 +> - `stretch`(默认值):轴线占满整个交叉轴。 + + + +## 四、项目的属性 + +以下6个属性设置在项目上。 + +> - `order` +> - `flex-grow` +> - `flex-shrink` +> - `flex-basis` +> - `flex` `flex-grow`, `flex-shrink` 和 `flex-basis`的简写 +> - `align-self` + + + +### 4.1 order属性 + +`order`属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。 + +> ```css +> .item { +> order: ; +> } +> ``` + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071013.png) + +### 4.2 flex-grow属性 + +`flex-grow`属性定义项目的放大比例,默认为`0`,即如果存在剩余空间,也不放大。 + +> ```css +> .item { +> flex-grow: ; /* default 0 */ +> } +> ``` + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071014.png) + +如果所有项目的`flex-grow`属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的`flex-grow`属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。 + +### 4.3 flex-shrink属性 + +`flex-shrink`属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。 + +> ```css +> .item { +> flex-shrink: ; /* default 1 */ +> } +> ``` + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071015.jpg) + +如果所有项目的`flex-shrink`属性都为1,当空间不足时,都将等比例缩小。如果一个项目的`flex-shrink`属性为0,其他项目都为1,则空间不足时,前者不缩小。 + +负值对该属性无效。 + +### 4.4 flex-basis属性 + +`flex-basis`属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为`auto`,即项目的本来大小。 + +> ```css +> .item { +> flex-basis: | auto; /* default auto */ +> } +> ``` + +它可以设为跟`width`或`height`属性一样的值(比如350px),则项目将占据固定空间。 + +### 4.5 flex属性 + +`flex`属性是`flex-grow`, `flex-shrink` 和 `flex-basis`的简写,默认值为`0 1 auto`。后两个属性可选。 + +> ```css +> .item { +> flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] +> } +> ``` + +该属性有两个快捷值:`auto` (`1 1 auto`) 和 none (`0 0 auto`)。 + +**建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。** + + + +### 4.6 align-self属性 + +`align-self`属性允许单个项目有与其他项目不一样的对齐方式,可覆盖`align-items`属性。默认值为`auto`,表示继承父元素的`align-items`属性,如果没有父元素,则等同于`stretch`。 + +> ```css +> .item { +> align-self: auto | flex-start | flex-end | center | baseline | stretch; +> } +> ``` + +![img](http://www.ruanyifeng.com/blogimg/asset/2015/bg2015071016.png) + +该属性可能取6个值,除了auto,其他都与align-items属性完全一致。 + + + +(完) + + + +> 来源: +> +> +> +> + +​ \ No newline at end of file diff --git a/docs/web/CSS-HTML/01.flex布局基础.md b/docs/ui/01.flex布局案例-基础.md similarity index 99% rename from docs/web/CSS-HTML/01.flex布局基础.md rename to docs/ui/01.flex布局案例-基础.md index b166055..15665d0 100644 --- a/docs/web/CSS-HTML/01.flex布局基础.md +++ b/docs/ui/01.flex布局案例-基础.md @@ -1,4 +1,4 @@ -# flex布局基础 +# flex布局案例-基础 先上代码,[效果](#效果)在后面 diff --git a/docs/web/CSS-HTML/02.flex布局案例-骰子.md b/docs/ui/02.flex布局案例-骰子.md similarity index 100% rename from docs/web/CSS-HTML/02.flex布局案例-骰子.md rename to docs/ui/02.flex布局案例-骰子.md diff --git a/docs/web/CSS-HTML/03.flex布局案例-网格布局.md b/docs/ui/03.flex布局案例-网格布局.md similarity index 100% rename from docs/web/CSS-HTML/03.flex布局案例-网格布局.md rename to docs/ui/03.flex布局案例-网格布局.md diff --git a/docs/web/CSS-HTML/04.flex布局案例-圣杯布局.md b/docs/ui/04.flex布局案例-圣杯布局.md similarity index 100% rename from docs/web/CSS-HTML/04.flex布局案例-圣杯布局.md rename to docs/ui/04.flex布局案例-圣杯布局.md diff --git a/docs/web/CSS-HTML/05.flex布局案例-输入框布局.md b/docs/ui/05.flex布局案例-输入框布局.md similarity index 100% rename from docs/web/CSS-HTML/05.flex布局案例-输入框布局.md rename to docs/ui/05.flex布局案例-输入框布局.md diff --git a/docs/web/CSS-HTML/06.CSS3之transform过渡.md b/docs/ui/06.CSS3之transform过渡.md similarity index 100% rename from docs/web/CSS-HTML/06.CSS3之transform过渡.md rename to docs/ui/06.CSS3之transform过渡.md diff --git a/docs/web/CSS-HTML/07.CSS3之animation动画.md b/docs/ui/07.CSS3之animation动画.md similarity index 100% rename from docs/web/CSS-HTML/07.CSS3之animation动画.md rename to docs/ui/07.CSS3之animation动画.md diff --git a/docs/web/CSS-HTML/README.md b/docs/web/CSS-HTML/README.md deleted file mode 100644 index 9e86a96..0000000 --- a/docs/web/CSS-HTML/README.md +++ /dev/null @@ -1 +0,0 @@ -> 敬请期待 \ No newline at end of file