1,搭建脚手架,脚手架自动帮忙提交一次代码到主分支。
2,git remote add origin git@github.com:lipenga/toutiao-m.git //添加一个远程仓库
3. git push --set-upstream origin master:master // 简写如下
3. git push -u origin master:master
4.安装vant postcss-pxtorem lib-flexible(设置rem基准值 根目录创建。postcssrc.js 复制粘贴)
5.搭建首页组件.描绘页面.
二.登陆
6.登陆成功。处理token,使用vuex,this.
s
t
o
r
e
.
c
o
m
m
i
t
(
′
s
e
t
U
s
e
r
′
,
d
a
t
a
.
d
a
t
a
)
,
通
过
调
用
s
e
t
U
s
e
r
,
将
t
o
k
e
n
传
到
s
t
a
t
e
:
/
/
w
i
n
d
o
w
.
l
o
c
a
l
S
t
o
r
a
g
e
.
s
e
t
I
t
e
m
(
′
t
o
k
e
n
′
,
J
S
O
N
.
s
t
r
i
n
g
i
f
y
(
s
t
a
t
e
.
u
s
e
r
)
)
7.
s
t
a
t
e
:
u
s
e
r
:
J
S
O
N
.
p
a
r
s
e
(
w
i
n
d
o
w
.
l
o
c
a
l
S
t
o
r
a
g
e
.
g
e
t
I
t
e
m
(
′
t
o
k
e
n
′
)
)
,
8
。
封
装
本
地
储
存
模
板
,
优
化
t
u
o
k
e
n
设
置
。
s
e
t
U
s
e
r
里
设
置
t
o
k
e
n
s
e
t
I
t
e
m
(
′
t
o
k
e
n
′
,
J
S
O
N
.
s
t
r
i
n
g
i
f
y
(
s
t
a
t
e
.
u
s
e
r
)
)
。
s
t
a
t
e
:
u
s
e
r
:
g
e
t
I
t
e
m
(
′
t
o
k
e
n
′
)
,
9.
绘
制
l
a
y
o
u
t
公
用
模
板
。
加
路
由
出
口
,
配
置
四
个
底
部
导
航
组
件
,
并
跳
转
。
10.
绘
制
h
o
m
e
页
,
顶
部
未
登
陆
状
态
,
点
击
回
退
到
登
录
页
。
绘
制
顶
部
登
陆
状
态
。
未
登
录
状
态
,
和
公
共
部
分
。
通
过
v
−
i
f
和
t
o
k
e
n
是
否
存
在
控
制
登
陆
和
未
登
陆
部
分
的
显
示
隐
藏
。
11.
点
击
退
出
。
清
空
t
o
k
e
n
:
t
h
i
s
.
store.commit('setUser', data.data),通过调用setUser,将token传到state: // window.localStorage.setItem('token', JSON.stringify(state.user)) 7. state: { user: JSON.parse(window.localStorage.getItem('token')) }, 8。封装本地储存模板,优化tuoken设置。setUser里设置token setItem('token', JSON.stringify(state.user))。 state: { user: getItem('token') }, 9.绘制layout公用模板。加路由出口,配置四个底部导航组件,并跳转。 10.绘制home页,顶部未登陆状态,点击回退到登录页。绘制顶部登陆状态。未登录状态,和公共部分。通过v-if和token是否存在控制登陆和未登陆部分的显示隐藏。 11.点击退出。清空token: this.
store.commit(′setUser′,data.data),通过调用setUser,将token传到state://window.localStorage.setItem(′token′,JSON.stringify(state.user))7.state:user:JSON.parse(window.localStorage.getItem(′token′)),8。封装本地储存模板,优化tuoken设置。setUser里设置tokensetItem(′token′,JSON.stringify(state.user))。state:user:getItem(′token′),9.绘制layout公用模板。加路由出口,配置四个底部导航组件,并跳转。10.绘制home页,顶部未登陆状态,点击回退到登录页。绘制顶部登陆状态。未登录状态,和公共部分。通过v−if和token是否存在控制登陆和未登陆部分的显示隐藏。11.点击退出。清空token:this.store.commit(‘setUser’, null)
12.获取用户信息。发请求需要添加请求头。再设置请求拦截器request.interceptors.request.use(
function(config) {
const { user } = store.state
if (user && user.token) {
config.headers.Authorization = Bearer ${user.token}
}
return config
},
function(error) {
return Promise.reject(error)
}
)
13.渲染用户信息到已登陆页
14.绘制频道标签。获取频道信息,发请求并渲染。内容部分采用组件加载。配合循环:新建内容组件。在home页导入。并注册,在循环中使用,在新建的组件中prop接收动态循环项。
15.绘制list页面。用list组件。该组件有load方法loading须理解才能好的运用。
16.下拉刷新数据。采用van-pull-refresh组件.用fixed固定搜索框,标签页。再用padding-top固定列表视图.
17.用cell单元格组件,配合插槽,渲染标题内容和时间还有图片。
18.调整样式。通过返回的type值,如果封面三张图。就flex布局和v-if显示到下方。一张图,就显示到右边。再用day.js处理相对时间
19.点击汉堡按钮。出现弹出框,弹出框中插入新的组件。新的组件用宫格绘制单元格。
20.父组件通过动态绑定,把channel项传给子组件,子组件通过props接收父组件的item数据项并渲染到单元格.
21.父组件动态绑定active到插槽上,子组件继续props接收。并判断index===active,正确则加类名active.
22.获取所有频道列表,减去已经得到得标签列表,是剩余的推荐列表。循环这个计算属性。渲染到推荐频道上面。
23.点击按钮,添加到我的标签。直接添加到我的已有标签数组里。计算属性自动计算并减去这个标签。
24.通过标志符来控制关闭按钮的显示和隐藏。和if来判断编辑和完成按钮的切换。推荐按钮
25.关闭按钮的位置不好控制。从标签分离出来,通过插槽来控制他的位置和显隐。
26.当标签点击时,如果不是编辑状态,跳转到对应的活动页,而且关闭弹框:
27.点击标签。触发函数。携带参数(参数是被点击的项)。再this.
e
m
i
t
传
递
给
父
组
件
t
h
i
s
.
emit传递给父组件 this.
emit传递给父组件this.emit(‘updataActive’, index),父组件在对应的组件标签里接收: @updataActive="onUpdataActive"接收并重新定义onUpdataActive函数。给active赋值。
28.未登陆状态添加标签在本地储存。登陆状态添加标签在线上请求。
29.获取真正频道列表,如果是登陆状态,则请求获取频道数据;如果未登录,则获取本地存储数据,存在则用。如本地无数据,请求获取推荐数据。
二。搜索模块
1.业务功能:联想建议,历史记录,搜索结果。
2.点击搜索。配置组件和路由。跳转到search页面
3.在页面下插入三个组件。联想建议,历史记录,搜索结果。判断输入框有无内容,用if-else-if来显示不同的组件
4.当搜索框变化时,显示联想建议。父组件搜索框绑定搜索值,并通过动态数据绑定传递给儿子组件,子组件props接收。并监听变化。
5.数据变化时,发送请求,请求携带变化的数据,将响应回来的数据赋值给列表,并在模板里渲染列表项。
6.防止每一次搜索按键都是一次请求。设计防抖,导入lodash,用debounce指定发送请求的间隔 handler: debounce(function(value) {
this.loadsuggess(value)
}, 1000)
7.关键字高亮,将关键字用v-html绑定,触发转换颜色函数,给输入关键字加样式使其高亮,函数中用正则匹配关键字,再用高亮字替换显示的文本中的关键字
8. hightlight(item) {
const hightlightstr = <span class='active'>${this.searchvalue}</span>
const reg = new RegExp(this.searchvalue, ‘gi’)
return item.replace(reg, hightlightstr)
}
9.在搜索结果组件动态绑定输入的值,组件里props接收结果,1.接收到结果后发送请求,获取数据;2.将数据添加到列表中,再循环渲染。3,关闭加载load 4,判断是否还有数据,有则改变数据请求下一页数据,没有就finished设置为结束。
10.滚动数据搜索框会消失,给搜索框加fixed定位。整个body加padding-top防止搜索框覆盖内容。
三:历史记录模块
11.点击回车搜索,将搜索框的关键字添加到新建的历史记录数组里,用indexOf判断,如果重复,则删除。
12.再将数组动态传递给子组件,组件props接收。渲染到搜索历史页面。
13.通过标志符控制垃圾桶和批量删除和单个删除的显示隐藏。点击历史记录数据,通过显隐符号判断是否为删除状态,如果是删除状态,用splice删除单个状态,批量删除用emit。
14.如果非删除状态,点击单个历史数据,添加到搜索框。可以直接绑定到onSearch事件上。
四:文章模块
1.新建文章组件,点击单篇文章,跳转到文章组件。组件的path路径为动态的,后面加个id。id为文章的id。
2.created钩子发请求,定义函数根据id获取信息,如果id超过2*53次方,需要用到json-bigint插件转换。消除404;
3.创建新的文章对象。储存响应数据。
4.实现图片预览,给内容加ref。数据驱动视图不是立即的,在延时器里定义函数,函数通过循环获取文章的图片src.储存到数组里。给图片的每一项设置点击事件,点击时,调用预览组件。
5.if-else控制关注用户取消用户按钮显示隐藏,点击按钮,发送请求,请求成功,更改按钮状态。
6.关注加:load=‘loading’,提高用户等待体验
7.封装组件,组件公用。创建vue组件,公共部分抽离,需要的数据和方法重新定义。在需要用的组件的页面important引入组件,和components注册,模板中引入。
8.在组件上使用v-model。v-model=“article.is_followed”,子组件input,发送请求,value是参数。 this.
e
m
i
t
(
′
i
n
p
u
t
′
,
!
t
h
i
s
.
v
a
l
u
e
)
9.
创
建
收
藏
组
件
,
导
入
,
替
换
。
在
父
组
件
v
−
m
o
d
e
l
绑
定
响
应
数
据
的
收
藏
,
子
组
件
p
r
o
p
s
接
收
v
a
l
u
e
,
通
过
判
断
v
a
l
u
e
的
t
u
r
e
和
f
a
l
s
e
来
控
制
收
藏
和
未
收
藏
的
样
式
。
10.
创
建
评
论
组
件
,
请
求
数
据
,
l
o
a
d
五
步
。
渲
染
到
页
面
。
动
态
绑
定
。
11.
点
击
评
论
,
弹
出
对
话
框
.
输
入
评
论
.
确
定
评
论
.
发
请
请
求
,
获
取
数
据
.
数
据
用
列
表
接
收
,
再
渲
染
处
理
.
12.
点
击
回
复
评
论
,
弹
出
对
话
框
.
(
这
中
间
有
涉
及
三
个
页
面
的
数
据
传
递
,
如
不
采
用
v
u
e
x
,
可
以
逐
级
传
递
.
找
父
组
件
即
可
.
)
,
调
用
评
论
组
件
,
显
示
全
部
评
论
.
13.
点
击
回
复
评
论
的
评
论
按
钮
。
跳
出
弹
出
框
。
弹
出
框
里
插
入
之
间
的
评
论
框
组
件
,
处
理
下
请
求
所
需
要
的
数
据
,
数
据
交
互
可
以
用
依
赖
注
入
或
者
v
u
e
x
,
和
原
始
的
v
u
e
逐
级
传
递
。
调
用
组
件
点
击
评
论
后
,
关
闭
弹
出
框
。
五
。
编
辑
1.
点
击
编
辑
,
新
建
编
辑
主
组
件
,
c
r
e
a
t
e
d
获
取
用
户
数
据
,
把
用
户
数
据
渲
染
到
主
编
辑
框
.
2.
点
击
编
辑
昵
称
,
弹
出
编
辑
昵
称
的
框
,
框
里
调
用
新
建
组
件
,
将
数
据
从
父
组
件
传
到
子
组
件
,
子
组
件
绑
定
到
输
入
框
,
发
请
请
求
,
完
成
修
改
。
3.
点
击
编
辑
生
日
,
弹
出
编
辑
生
日
的
框
,
框
里
调
用
新
建
组
件
,
用
父
组
件
用
v
−
m
o
d
e
l
将
生
日
传
到
子
组
件
,
子
组
件
用
v
a
l
u
e
接
收
。
然
后
将
v
a
l
u
e
设
置
当
前
c
u
r
r
e
n
t
时
间
。
发
送
请
求
.
参
数
为
当
前
时
间
。
4.
请
求
成
功
后
将
b
i
r
t
h
d
a
y
反
向
绑
定
给
v
−
m
o
d
e
l
=
=
>
使
用
t
h
i
s
.
emit('input', !this.value) 9.创建收藏组件,导入,替换。在父组件v-model绑定响应数据的收藏,子组件props接收value,通过判断value的ture和false来控制收藏和未收藏的样式。 10.创建评论组件,请求数据,load五步。渲染到页面。动态绑定。 11.点击评论,弹出对话框.输入评论.确定评论.发请请求,获取数据.数据用列表接收,再渲染处理. 12.点击回复评论,弹出对话框.(这中间有涉及三个页面的数据传递,如不采用vuex ,可以逐级传递.找父组件即可.),调用评论组件,显示全部评论. 13.点击回复评论的评论按钮。跳出弹出框。弹出框里插入之间的评论框组件,处理下请求所需要的数据,数据交互可以用依赖注入或者vuex,和原始的vue逐级传递。调用组件点击评论后,关闭弹出框。 五。编辑 1.点击编辑,新建编辑主组件,created获取用户数据,把用户数据渲染到主编辑框. 2.点击编辑昵称,弹出编辑昵称的框,框里调用新建组件,将数据从父组件传到子组件,子组件绑定到输入框,发请请求,完成修改。 3.点击编辑生日,弹出编辑生日的框,框里调用新建组件,用父组件用v-model将生日传到子组件,子组件用value接收。然后将value设置当前current时间。发送请求.参数为当前时间。 4.请求成功后 将birthday反向绑定给v-model==>使用this.
emit(′input′,!this.value)9.创建收藏组件,导入,替换。在父组件v−model绑定响应数据的收藏,子组件props接收value,通过判断value的ture和false来控制收藏和未收藏的样式。10.创建评论组件,请求数据,load五步。渲染到页面。动态绑定。11.点击评论,弹出对话框.输入评论.确定评论.发请请求,获取数据.数据用列表接收,再渲染处理.12.点击回复评论,弹出对话框.(这中间有涉及三个页面的数据传递,如不采用vuex,可以逐级传递.找父组件即可.),调用评论组件,显示全部评论.13.点击回复评论的评论按钮。跳出弹出框。弹出框里插入之间的评论框组件,处理下请求所需要的数据,数据交互可以用依赖注入或者vuex,和原始的vue逐级传递。调用组件点击评论后,关闭弹出框。五。编辑1.点击编辑,新建编辑主组件,created获取用户数据,把用户数据渲染到主编辑框.2.点击编辑昵称,弹出编辑昵称的框,框里调用新建组件,将数据从父组件传到子组件,子组件绑定到输入框,发请请求,完成修改。3.点击编辑生日,弹出编辑生日的框,框里调用新建组件,用父组件用v−model将生日传到子组件,子组件用value接收。然后将value设置当前current时间。发送请求.参数为当前时间。4.请求成功后将birthday反向绑定给v−model==>使用this.emit(‘input’, birthday)
5.更新头像。点击头像,弹出type为file的文件input框,给其添加ref引用,设置隐藏,在头像上设置点击,手动调用点击事件,@click='
r
e
f
s
.
x
x
x
.
c
l
i
c
k
(
)
6.
在
i
n
p
u
t
设
置
c
h
a
n
g
e
事
件
,
发
生
改
变
,
拿
到
改
变
的
图
片
的
信
息
。
c
o
n
s
t
f
i
l
e
=
t
h
i
s
.
refs.xxx.click() 6.在input设置change事件,发生改变,拿到改变的图片的信息 。 const file = this.
refs.xxx.click()6.在input设置change事件,发生改变,拿到改变的图片的信息。constfile=this.refs.xxx.files[0]
// 基于文件对象获取blob数据
const data = window.URL.createObjectURL(file)
7.选定图片后,显示弹出框,弹出框的内容用组件显示。组件里img的src属性为父组件传递过来的 this.imgdata = window.URL.createObjectURL(file)
8.图片裁切,下载cropperjs依赖并导入。import ‘cropperjs/dist/cropper.css’;import Cropper from ‘cropperjs’,在mounted里配置选项。此时裁切框已经出现。
9.点击确定时,基于服务器的裁切使用getData方法获取裁切参数:console.log(this.cropper.getData())
10: 纯客户端的裁切使用getCroppedCanvas获取裁切的文件对象: this.cropper.getCroppedCanvas().toBlob(blob => {
console.log(blob)
})
11.