编辑文章
1). 添加编辑文章路由
打开 src/router/routes.js
文件,在数组的最后面,添加编辑文章路由 Edit
:
src/router/routes.js
1 // Edit 2 { 3 path: '/articles/:articleId/edit', 4 name: 'Edit', 5 component: () => import('@/views/articles/Create'), 6 meta: { auth: true } 7 },
我们这里使用了跟 Create
一样的组件页面,但将其路径指向一个新的地址,并命名为 Edit
。
2). 添加编辑入口
1、打开 src/views/articles/Content.vue
文件,复制以下代码替换原 <script>
:
src/views/articles/Content.vue
1 <script> 2 import SimpleMDE from 'simplemde' 3 import hljs from 'highlight.js' 4 import emoji from 'node-emoji' 5 import { mapState } from 'vuex' 6 7 export default { 8 name: 'Content', 9 data() { 10 return { 11 title: '', // 文章标题 12 content: '', // 文章内容 13 date: '', // 文章创建时间 14 uid: 1 // 用户 ID 15 } 16 }, 17 computed: { 18 ...mapState([// 将仓库的以下状态混入到计算属性之中 19 'auth', 20 'user' 21 ]) 22 }, 23 created() { 24 const articleId = this.$route.params.articleId 25 const article = this.$store.getters.getArticleById(articleId) 27 if (article) { 28 let { uid, title, content, date } = article 30 this.uid = uid 31 this.title = title 32 this.content = SimpleMDE.prototype.markdown(emoji.emojify(content, name => name)) 33 this.date = date 35 this.$nextTick(() => { 36 this.$el.querySelectorAll('pre code').forEach((el) => { 37 hljs.highlightBlock(el) 38 }) 39 }) 40 } 41 42 this.articleId = articleId 43 }, 44 methods: { 45 editArticle() { 46 this.$router.push({ name: 'Edit', params: { articleId: this.articleId } }) 47 }, 48 deleteArticle() { 49 50 } 51 } 52 } 53 </script>
2、查找 <div class="markdown-body"
,在其后面添加『编辑删除图标』:
1 <div class="markdown-body" v-html="content"></div> 2 3 <!-- 编辑删除图标 --> 4 <div v-if="auth && uid === 1" class="panel-footer operate"> 5 <div class="actions"> 6 <a @click="deleteArticle" class="admin" href="javascript:;"><i class="fa fa-trash-o"></i></a> 7 <a @click="editArticle" class="admin" href="javascript:;"><i class="fa fa-pencil-square-o"></i></a> 8 </div> 9 </div>
我们先判断用户是否已登录,已登录且当前用户 ID 为 1
时,渲染编辑删除图标。
3). 添加编辑文章逻辑
打开 src/store/actions.js
文件,复制以下代码替换原 post
事件:
src/store/actions.js
1 export const post = ({ commit, state }, { article, articleId }) => { 2 let articles = state.articles 3 4 if (!Array.isArray(articles)) articles = [] 5 6 if (article) { 7 const uid = 1 8 const { title, content } = article 9 const date = new Date() 10 11 if (articleId === undefined) { 12 const lastArticle = articles[articles.length - 1] 13 14 if (lastArticle) { 15 articleId = parseInt(lastArticle.articleId) + 1 16 } else { 17 articleId = articles.length + 1 18 } 19 20 articles.push({ 21 uid, 22 articleId, 23 title, 24 content, 25 date 26 }) 27 }else { // 如果有传 articleId 28 // 遍历所有文章 29 for (let article of articles) { 30 // 找到与 articleId 对应的文章 31 if (parseInt(article.articleId) === parseInt(articleId)) { 32 // 更新文章的标题 33 article.title = title 34 // 更新文章的内容 35 article.content = content 36 break 37 } 38 } 39 } 40 commit('UPDATE_ARTICLES', articles) 41 router.push({ name: 'Content', params: { articleId, showMsg: true } }) 42 } 43 }
4). 修改创作文章页面
1、打开 src/views/articles/Create.vue
文件,复制以下代码替换原 <script>
:
src/views/articles/Create.vue
1 <script> 2 import SimpleMDE from 'simplemde' 3 import hljs from 'highlight.js' 4 import ls from '@/utils/localStorage' 5 6 window.hljs = hljs 7 8 export default { 9 name: 'Create', 10 data() { 11 return { 12 articleId: undefined // 文章 ID 13 } 14 }, 15 beforeRouteEnter(to, from, next) { 16 next(vm => { 17 // 确认渲染组件的对应路由时,设置 articleId 18 vm.setArticleId(vm.$route.params.articleId) 19 }) 20 }, 21 // 在离开该组件的对应路由前 22 beforeRouteLeave(to, from, next) { 23 // 清空自动保存的文章数据 24 this.clearData() 25 next() 26 }, 27 watch: { 28 // 监听路由参数的变化 29 '$route'(to) { 30 // 清空自动保存的文章数据 31 this.clearData() 32 // 设置 articleId 33 this.setArticleId(to.params.articleId) 34 } 35 }, 36 methods: { 37 // 填充文章数据 38 fillContent(articleId) { 39 // 编辑器 40 const simplemde = this.simplemde 41 // 自动保存的标题 42 const smde_title = ls.getItem('smde_title') 43 44 // 有 articleId 时 45 if (articleId !== undefined) { 46 // 获取对应文章 47 const article = this.$store.getters.getArticleById(articleId) 48 49 if (article) { 50 // 获取文章的标题和内容 51 const { title, content } = article 52 53 // 有自动保存的标题时,使用自动保存的标题,否则使用文章的标题 54 this.title = smde_title || title 55 // 有自动保存的内容时,使用自动保存的内容,否则使用文章的内容 56 this.content = simplemde.value() || content 57 // 设置编辑器的内容 58 simplemde.value(this.content) 59 } 60 } else { // 没有 articleId 时,使用自动保存的标题和内容 61 this.title = smde_title 62 this.content = simplemde.value() 63 } 64 }, 65 post() { 66 if (title !== '' && content.trim() !== '') { 67 // 在分发 post 事件时,附带 articleId 参数 68 this.$store.dispatch('post', { article, articleId: this.articleId }) 69 } 70 }, 71 // 设置 articleId 72 setArticleId(articleId) { 73 // 获取 localStorage 保存的 articleId,临时用它来判断是否还处于当前编辑页面 74 const localArticleId = ls.getItem('articleId') 75 76 // 手动在两个不同的编辑页面之间跳转时(如 /articles/1/edit 和 /articles/2/edit)时 77 if (articleId !== undefined && !(articleId === localArticleId)) { 78 // 清空自动保存的文章数据 79 this.clearData() 80 } 81 82 // 设置当前实例的 articleId 83 this.articleId = articleId 84 // 填充文章数据 85 this.fillContent(articleId) 86 // 在 localStorage 保存一个 articleId 87 ls.setItem('articleId', articleId) 88 } 89 } 90 </script>
上面代码的核心就是利用路由参数的 articleId
,获取对应的文章数据,来填充编辑页面。在不同的编辑页面之间跳转(如 /articles/1/edit
和 /articles/2/edit
)时,因为我们会优先填充自动保存的数据,所以需要先调用 clearData
清空它们。
涉及的新知识点:
1 beforeRouteLeave(to, from, next) { 2 this.clearData() 3 next() 4 }
beforeRouteLeave
是组件内的守卫,在离开该组件的对应路由时调用,此时可以访问 this
,需要使用 next()
确认导航。
监听 '$route'
:
1 watch: { 2 '$route'(to) { 3 this.clearData() 4 this.setArticleId(to.params.articleId) 5 } 6 }
我们可以通过监听 '$route'
来得知路由参数的变化,我们通常会在两个路由都渲染相同的组件时监听 '$route'
,这是因为 Vue 会复用组件实例,以导致组件内的部分钩子不再被调用。举例来说,我们的『编辑文章』和 『创作文章』都使用 Create.vue
组件,当我们从『编辑文章』导航到『创作文章』时(在编辑文章页面点击创作文章按钮),beforeRouteEnter
就不会被调用,所以我们需要监听 '$route'
,以响应路由参数的变化。
2、查找 <h2 class="text-center">
,作如下修改:
1 <!-- 修改 --> 2 <h2 class="text-center">创作文章</h2> 3 <!-- 为 --> 4 <h2 class="text-center">{{ articleId ? '编辑文章' : '创作文章' }}</h2>