前言:在最近学习 Vue.js 的时候,看到国外一篇讲述了如何使用 Vue.js 和 Vuex 来构建一个简单笔记的单页应用的文章。感觉收获挺多,自己在它的例子的基础上进行了一些优化和自定义功能,在这里和大家分享下学习心得。

在这篇教程中我们将通过构建一个笔记应用来学习如何在我们的 Vue 项目中使用 Vuex。我们将大概的过一遍什么是 Vuex.js,在项目中什么时候使用它,和如何构建我们的 Vue 应用。

这里放一张我们项目的预览图片:

项目源码:vuex-notes-app;有需要的同学可以直接下载源码查看。

主要知识点

  • Vuex 状态管理机制的使用

  • Vue.js 的基础 api

  • Vue-cli 脚手架的安装及使用

  • vur-router 的使用

  • ES6 的语法,这里推荐看下阮一峰的入门教程

Vuex 概述

在我们迫不及待的开始项目之前,我们最好先花几分钟来了解下 Vuex 的核心概念。

Vuex 是一个专门为 Vue.js 应用所设计的集中式状态管理架构。它借鉴了 Flux 和 Redux 的设计思想,但简化了概念,并且采用了一种为能更好发挥 Vue.js 数据响应机制而专门设计的实现。

state 这样概念初次接触的时候可能会感觉到有点模糊,简单来说就是将 state 看成我们项目中使用的数据的集合。然后,Vuex 使得 组件本地状态(component local state)和 应用层级状态(application state) 有了一定的差异。

  • component local state:该状态表示仅仅在组件内部使用的状态,有点类似通过配置选项传入 Vue 组件内部的意思。

  • application level state:应用层级状态,表示同时被多个组件共享的状态层级。

假设有这样一个场景:我们有一个父组件,同时包含两个子组件。父组件可以很容易的通过使用 props 属性来向子组件传递数据。

但是问题来了,当我们的两个子组件如何和对方互相通信的? 或者子组件如何传递数据给他父组件的?在我们的项目很小的时候,这个两个问题都不会太难,因为我们可以通过事件派发和监听来完成父组件和子组件的通信。

然而,随着我们项目的增长:

  • 保持对所有的事件追踪将变得很困难。到底哪个事件是哪个组件派发的,哪个组件该监听哪个事件?

  • 项目逻辑分散在各个组件当中,很容易导致逻辑的混乱,不利于我们项目的维护。

  • 父组件将变得和子组件耦合越来越严重,因为它需要明确的派发和监听子组件的某些事件。

这就是 Vuex 用来解决的问题。 Vuex 的四个核心概念分别是:

  • The state tree:Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态。至此它便作为一个『唯一数据源(SSOT)』而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

  • Getters:用来从 store 获取 Vue 组件数据。

  • Mutators:事件处理器用来驱动状态的变化。

  • Actions:可以给组件使用的函数,以此用来驱动事件处理器 mutations

如何你暂时还不太理解这个四个概念,不用着急,我们将在后面的项目实战中详细的解释。

下面这张图详细的解释了 Vuex 应用中数据的流向(Vuex 官方图)

简单解释下:

Vuex 规定,属于应用层级的状态只能通过 Mutation 中的方法来修改,而派发 Mutation 中的事件只能通过 action。

从左到又,从组件出发,组件中调用 action,在 action 这一层级我们可以和后台数据交互,比如获取初始化的数据源,或者中间数据的过滤等。然后在 action 中去派发 Mutation。Mutation 去触发状态的改变,状态的改变,将触发视图的更新。

注意事项

  • 数据流都是单向的

  • 组件能够调用 action

  • action 用来派发 Mutation

  • 只有 mutation 可以改变状态

  • store 是响应式的,无论 state 什么时候更新,组件都将同步更新

环境安装

这个应用将使用 webpack 来做模块打包,处理和热重启。使用 Vue 官方提供的脚手架 vue-cli

安装 vue-cli

  1. npm install -g vue-cli

注:Node.js >= 4.x, 5.x 最好

初始化应用

  1. vue init webpack vue-notes-app
  2. cd vue-notes-app
  3. npm install // 安装依赖包
  4. npm run dev // 启动服务

初始化一个项目名为vue-notes-app的应用,并选择使用 webpack 打包方式。在命令行中按照提示选择初始化配置项。其中在选择 JSLint 校验的时候,推荐选择 AirBNB 规范。

使用你最喜欢的编辑器打开我们刚刚新建的项目,项目的结构大概如下图:

  • components/ 文件夹用来存放我们的 Vue 组件

  • vuex/ 文件夹存放的是和 Vuex store 相关的东西(state object,actions,mutators)

  • build/ 文件是 webpack 的打包编译配置文件

  • config/ 文件夹存放的是一些配置项,比如我们服务器访问的端口配置等

  • dist/ 该文件夹一开始是不存在,在我们的项目经过 build 之后才会产出

  • App.vue 根组件,所有的子组件都将在这里被引用

  • index.html 整个项目的入口文件,将会引用我们的根组件 App.vue

  • main.js 入口文件的 js 逻辑,在 webpack 打包之后将被注入到 index.html 中

功能模块

  • 新增笔记,新增一篇笔记,编辑区显示空的笔记内容

  • 删除笔记,删除一篇笔记之后,编辑区域显示当前笔记类别的第一项

  • 笔记列表切换,分为全部笔记和收藏笔记两种,在切换之后,编辑区域显示当前列表的第一条笔记

  • 收藏笔记,给当前激活的笔记打上收藏的标签

项目组件划分

在这个项目中,我们将总共使用四个组件:根组件 App.vue,操作栏组件 Toolbar.vue,别表组件 NotesList.vue,笔记编辑组件 Editor.vue。

创建 Vuex Store

按照上面我们列出来的功能模块,我们在 Vuex/ 下面建立一个 store.js 文件。

  1. import Vue from 'vue';
  2. import Vuex from 'vuex';
  3. Vue.use(Vuex);
  4. // 需要维护的状态
  5. const state = {
  6. notes: [],
  7. activeNote: {},
  8. show: ''
  9. };
  10. const mutations = {
  11. // 初始化 state
  12. INIT_STORE(state, data) {
  13. state.notes = data.notes,
  14. state.show = data.show;
  15. state.activeNote = data.activeNote;
  16. },
  17. // 新增笔记
  18. NEW_NOTE(state) {
  19. var newNote = {
  20. id: +new Date(),
  21. title: '',
  22. content: '',
  23. favorite: false
  24. };
  25. state.notes.push(newNote);
  26. state.activeNote = newNote;
  27. },
  28. // 修改笔记
  29. EDIT_NOTE(state, note) {
  30. state.activeNote = note;
  31. // 修改原始数据
  32. for (var i = 0; i < state.notes.length; i++) {
  33. if(state.notes[i].id === note.id){
  34. state.notes[i] = note;
  35. break;
  36. }
  37. };
  38. },
  39. // 删除笔记
  40. DELETE_NOTE(state) {
  41. state.notes.$remove(state.activeNote);
  42. state.activeNote = state.notes[0] || {};
  43. },
  44. // 切换笔记的收藏与取消收藏
  45. TOGGLE_FAVORITE(state) {
  46. state.activeNote.favorite = !state.activeNote.favorite;
  47. },
  48. // 切换显示数据列表类型:全部 or 收藏
  49. SET_SHOW_ALL(state, show){
  50. state.show = show;
  51. // 切换数据展示,需要同步更新 activeNote
  52. if(show === 'favorite'){
  53. state.activeNote = state.notes.filter(note => note.favorite)[0] || {};
  54. }else{
  55. state.activeNote = state.notes[0] || {};
  56. }
  57. },
  58. // 设置当前激活的笔记
  59. SET_ACTIVE_NOTE(state, note) {
  60. state.activeNote = note;
  61. }
  62. };
  63. export default new Vuex.Store({
  64. state,
  65. mutations
  66. });

创建 Vuex Actions

在 Vuex/ 下面建立一个 action.js,用来给组件使用的函数。

  1. function makeAction(type) {
  2. return ({ dispatch }, ...args) => dispatch(type, ...args);
  3. };
  4. const initNote = {
  5. id: +new Date(),
  6. title: '我的笔记',
  7. content: '第一篇笔记内容',
  8. favorite: false
  9. };
  10. // 模拟初始化数据
  11. const initData = {
  12. show: 'all',
  13. notes: [initNote],
  14. activeNote: initNote
  15. };
  16. export const initStore = ({ dispatch }) => {
  17. dispatch('INIT_STORE', initData);
  18. };
  19. // 更新当前activeNote对象
  20. export const updateActiveNote = makeAction('SET_ACTIVE_NOTE');
  21. // 添加一个note对象
  22. export const newNote = makeAction('NEW_NOTE');
  23. // 删除一个note对象
  24. export const deleteNote = makeAction('DELETE_NOTE');
  25. export const toggleFavorite = makeAction('TOGGLE_FAVORITE');
  26. export const editNote = makeAction('EDIT_NOTE');
  27. // 更新列表展示
  28. export const updateShow = makeAction('SET_SHOW_ALL');

创建 Vuex Getters

在 vuex/ 下面建立一个 getter.js 文件,用来从 store 获取数据。

  1. // 获取 noteList,这里将会根据 state.show 的状态做数据过滤
  2. export const filteredNotes = (state) => {
  3. if(state.show === 'all'){
  4. return state.notes || {};
  5. }else if(state.show === 'favorite'){
  6. return state.notes.filter(note => note.favorite) || {};
  7. }
  8. };
  9. // 获取列表展示状态 : all or favorite
  10. export const show = (state) => {
  11. return state.show;
  12. };
  13. // 获取当前激活 note
  14. export const activeNote = (state) => {
  15. return state.activeNote;
  16. };

以上就是我们 Vuex 的所有逻辑了,在定下了我们需要完成的功能之后,接下来就是只需要在组件中去调用 action 来实现对应的功能了。

路由配置

在这里我们将使用 vue-router 来做路由,引用 bootstrap 样式。

index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>vuex-notes-app</title>
  6. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  7. </head>
  8. <body>
  9. <div id="app"></div>
  10. <!-- built files will be auto injected -->
  11. </body>
  12. </html>

所有的入口逻辑我们都将在 main.js 中编写

main.js

  1. import Vue from 'vue';
  2. import App from './App';
  3. import VueRouter from 'vue-router';
  4. import VueResource from 'vue-resource';
  5. // 路由模块和HTTP模块
  6. Vue.use(VueResource);
  7. Vue.use(VueRouter);
  8. const router = new VueRouter();
  9. router.map({
  10. '/index': {
  11. component: App
  12. }
  13. });
  14. router.redirect({
  15. '*': '/index'
  16. });
  17. router.start(App, '#app');

根组件 App.vue

  1. <template>
  2. <div id="app" class="app">
  3. <toolbar></toolbar>
  4. <notes-list></notes-list>
  5. <editor></editor>
  6. </div>
  7. </template>
  8. <style>
  9. html, #app {
  10. height: 100%;
  11. }
  12. body {
  13. margin: 0;
  14. padding: 0;
  15. border: 0;
  16. height: 100%;
  17. max-height: 100%;
  18. position: relative;
  19. }
  20. </style>
  21. <script>
  22. import Toolbar from './components/Toolbar';
  23. import NotesList from './components/NotesList';
  24. import Editor from './components/Editor';
  25. import store from './vuex/store';
  26. import { initStore } from './vuex/actions';
  27. export default {
  28. components: {
  29. Toolbar,
  30. NotesList,
  31. Editor
  32. },
  33. store,
  34. vuex: {
  35. actions: {
  36. initStore
  37. }
  38. },
  39. ready() {
  40. this.initStore()
  41. }
  42. }
  43. </script>

在根组件中引用了三个子组件:Toolbar.vue, NotesList.vue, Editor.vue。

注意:我们在配置里面加入了 vuex 这么一个选项,这里用来将我们 action 里面定义的方法给暴露出来,我们在根组件中只做了一件事情,那就是初始化模拟数据,因此我们在组件生命周期的 ready 阶段调用了 actions 里面的 initStore 来初始化我们的 store 里面的 state

Toolbar.vue

  1. <template>
  2. <div id="toolbar">
  3. <i class="glyphicon logo"><img src="../assets/logo.png" width="30" height="30"></i>
  4. <i @click="newNote" class="glyphicon glyphicon-plus"></i>
  5. <i @click="toggleFavorite" class="glyphicon glyphicon-star" :class="{starred: activeNote.favorite}"></i>
  6. <i @click="deleteNote" class="glyphicon glyphicon-remove"></i>
  7. </div>
  8. </template>
  9. <script>
  10. import { newNote, deleteNote, toggleFavorite } from '../vuex/actions';
  11. import { activeNote } from '../vuex/getters';
  12. export default {
  13. vuex: {
  14. getters: {
  15. activeNote
  16. },
  17. actions: {
  18. newNote,
  19. deleteNote,
  20. toggleFavorite
  21. }
  22. }
  23. }
  24. </script>
  25. <style lang="scss" scoped>
  26. #toolbar{
  27. float: left;
  28. width: 80px;
  29. height: 100%;
  30. background-color: #30414D;
  31. color: #767676;
  32. padding: 35px 25px 25px 25px;
  33. .starred {
  34. color: #F7AE4F;
  35. }
  36. i{
  37. font-size: 30px;
  38. margin-bottom: 35px;
  39. cursor: pointer;
  40. opacity: 0.8;
  41. transition: opacity 0.5s ease;
  42. &:hover{
  43. opacity: 1;
  44. }
  45. }
  46. }
  47. </style>

在这里,我们用到了 Vuex 的一个案例就是我们需要知道当前的激活的笔记是否是收藏类别的,如果是,我们需要高亮收藏按钮,那么如何知道呢?那就是通过 vuex 里面的 getters 获取当前激活的笔记对象,判断它的 favorite 是否为 true。

始终牢记一个概念,vuex 中数据是单向的,只能从 store 获取,而我们这个例子中的 activeNote 也是始终都在 store.js 中维护的,这样子就可以给其他组件公用了

  1. // 需要维护的状态
  2. const state = {
  3. notes: [],
  4. activeNote: {},
  5. show: ''
  6. };

NotesList.vue

  1. <template>
  2. <div id="notes-list">
  3. <div id="list-header">
  4. <h2>Notes | heavenru.com</h2>
  5. <div class="btn-group btn-group-justified" role="group">
  6. <!-- all -->
  7. <div class="btn-group" role="group">
  8. <button type="button" class="btn btn-default"
  9. @click="toggleShow('all')"
  10. :class="{active: show === 'all'}">All Notes</button>
  11. </div>
  12. <!-- favorites -->
  13. <div class="btn-group" role="group">
  14. <button type="button" class="btn btn-default"
  15. @click="toggleShow('favorite')"
  16. :class="{active: show === 'favorite'}">Favorites</button>
  17. </div>
  18. </div>
  19. </div>
  20. <!-- 渲染笔记列表 -->
  21. <div class="container">
  22. <div class="list-group">
  23. <a v-for="note in filteredNotes"
  24. class="list-group-item" href="#"
  25. :class="{active: activeNote === note}"
  26. @click="updateActiveNote(note)">
  27. <h4 class="list-group-item-heading">
  28. {{note.title.trim().substring(0,30)}}
  29. </h4>
  30. </a>
  31. </div>
  32. </div>
  33. </div>
  34. </template>
  35. <script>
  36. import { updateActiveNote, updateShow } from '../vuex/actions';
  37. import { show, filteredNotes, activeNote } from '../vuex/getters';
  38. export default {
  39. vuex: {
  40. getters: {
  41. show,
  42. filteredNotes,
  43. activeNote
  44. },
  45. actions: {
  46. updateActiveNote,
  47. updateShow
  48. }
  49. },
  50. methods: {
  51. toggleShow(show) {
  52. this.updateShow(show);
  53. }
  54. }
  55. }
  56. </script>

笔记列表组件,主要有三个操作

  • 渲染笔记

  • 切换渲染笔记

  • 点击列表 title,切换 activeNote

我们通过 getters 中的 filteredNotes 方法获取笔记列表

  1. // 获取 noteList,这里将会根据 state.show 的状态做数据过滤
  2. export const filteredNotes = (state) => {
  3. if(state.show === 'all'){
  4. return state.notes || {};
  5. }else if(state.show === 'favorite'){
  6. return state.notes.filter(note => note.favorite) || {};
  7. }
  8. };

可以看到,我们获取的列表是依赖于 state.show 这个状态的。而我们的切换列表操作恰好就是调用 actions 里面的方法来更新 state.show,这样一来,实现了数据列表的动态刷新,而且我们对树的操作都是通过调用 actions 的方法来实现的。

我们再看,在切换列表的时候,我们还需要动态的更新 activeNote。看看我们在 store.js 中是如何做的:

  1. // 切换显示数据列表类型:全部 or 收藏
  2. SET_SHOW_ALL(state, show){
  3. state.show = show;
  4. // 切换数据展示,需要同步更新 activeNote
  5. if(show === 'favorite'){
  6. state.activeNote = state.notes.filter(note => note.favorite)[0] || {};
  7. }else{
  8. state.activeNote = state.notes[0] || {};
  9. }
  10. }

触发这些操作的是我们给两个按钮分别绑定了我们自定义的函数,通过给函数传入不同的参数,然后调用 actions 里面的方法,来实现对数据的过滤,更新。

Editor.vue

  1. <template>
  2. <div id="note-editor">
  3. <div class="form-group">
  4. <input type="text" name="title"
  5. class="title form-control"
  6. placeholder="请输入标题"
  7. @input="updateNote"
  8. v-model="currentNote.title">
  9. <textarea
  10. v-model="currentNote.content" name="content"
  11. class="form-control" row="3" placeholder="请输入正文"
  12. @input="updateNote"></textarea>
  13. </div>
  14. </div>
  15. </template>
  16. <script>
  17. import { editNote } from '../vuex/actions';
  18. import { activeNote } from '../vuex/getters';
  19. export default {
  20. vuex: {
  21. getters: {
  22. activeNote
  23. },
  24. actions: {
  25. editNote
  26. }
  27. },
  28. computed: {
  29. // 通过计算属性得到的一个对象,这样子我们就能愉快的使用 v-model 了
  30. currentNote: activeNote
  31. },
  32. methods: {
  33. // 为什么这么做? 因为在严格模式中不允许直接在模板层面去修改 state 中的值
  34. updateNote() {
  35. this.editNote(this.currentNote);
  36. }
  37. }
  38. }
  39. </script>

在 Editor.vue 组件中,我们需要能够实时的更新当前的 activeNote 组件和列表中对应的我们正在修改的笔记对象的内容。

由于我们前面提到过,在组件中是不允许直接修改 store.js在里面的状态值的,所以在这里的时候,我们通过一个计算属性,将 store 里面的状态值赋值给一个对象,然后在自定义的 updateNotes() 方法中,去调用 action,同时传入 currentNote 对象。

在 store.js 中,我们是这么做的,找到对应的 id 的对象,重新赋值,因为前面提到过,我们的数据是响应式的,在这里进行了改变,对应的视图也将刷新改变,这样一来就实现了实时编辑,实时渲染的功能了。

  1. // 修改笔记
  2. EDIT_NOTE(state, note) {
  3. state.activeNote = note;
  4. // 修改原始数据
  5. for (var i = 0; i < state.notes.length; i++) {
  6. if(state.notes[i].id === note.id){
  7. state.notes[i] = note;
  8. break;
  9. }
  10. };
  11. },

Q&A

在这个项目中,我们并没有引入 vue-resource 插件,只是自己模拟了部分的数据,有兴趣的同学可以自己去试试。

由于我们的例子相对简单,没有涉及到很深入的东西,更深层次的研究需要大家花更多的时间去实践了。

最后,再说一句,在 action 里面,我们其实可以做的还有更多,比如根据 id 动态的异步获取笔记内容等等,这些有兴趣的同学可以自己去尝试,一点点的丰富这个例子。

vuex和vuejs的更多相关文章

  1. 探索 vuex 2.0 以及使用 vuejs 2.0 + vuex 2.0 构建记事本应用

    前言 首先说明这并不是一个教程贴,而记事本应用是网上早有的案例,对于学习 vuex 非常有帮助.我的目的是探索 vuex 2.0 ,然后使用 vue 2.0 + vuex 2.0 重写这个应用,其中最 ...

  2. vuejs学习——vue+vuex+vue-router项目搭建(三)

    前言 vuejs学习——vue+vuex+vue-router项目搭建(一) vuejs学习——vue+vuex+vue-router项目搭建(二) 为什么用vuex:组件之间的作用域独立,而组件之间 ...

  3. vuejs学习——vue+vuex+vue-router项目搭建(二)

    前言 最近比较忙,所有第二章发布晚了,不好意思各位. vuejs学习——vue+vuex+vue-router项目搭建(一) 中我们搭建好了vue项目,我相信大家已经体验了vue其中的奥妙了,接下来我 ...

  4. vuejs学习——vue+vuex+vue-router项目搭建(一)

    前言 快年底了却有新公司邀请了我,所以打算把上家公司的学到一下技术做一些总结和分享. 现在vuejs都2.0了,我相信也有很多朋友和我一样实际项目还是选择vue1.0的或者给新手一些参考,不管在选择哪 ...

  5. 探索 vuex 2.0 以及使用 vuejs 2.0 + vuex 2.0 构建记事本应用23

    前言 首先说明这并不是一个教程贴,而记事本应用是网上早有的案例,对于学习 vuex 非常有帮助.我的目的是探索 vuex 2.0 ,然后使用 vuejs 2.0 + vuex 2.0 重写这个应用,其 ...

  6. Vuex(一)——vuejs的状态管理模式

    一.Vuex是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式. 它采用集中式存储 管理 应用的所有组件 的 状态,并以 相应的规则 保证 状态以一种 可预测的方式 发生变化. ...

  7. VueJs(14)---理解Vuex

    理解Vuex 一.Vuex 是什么? 首先我们来分析一种实际开发中用vue.js的场景,你有n个组件,当你改变一个组件数据的时候需要同时改变其它n个组件的数据,那么我想你可能会对 vue 组件之间的通 ...

  8. vuejs组件交互 - 03 - vuex状态管理实现组件交互

    组件交互模式的使用场景 简单应用直接使用props down,event up的模式就可以了 小型应用使用事件中心模式即可 中大型应用使用vuex的状态管理模式 vuex 包含要管理的应用数据和更新数 ...

  9. 【转】VueJS中学习使用Vuex详解

    转自:https://segmentfault.com/a/1190000015782272   在SPA单页面组件的开发中 Vue的vuex和React的Redux 都统称为同一状态管理,个人的理解 ...

随机推荐

  1. IOS-适配iOS10以及Xcode8

    现在在苹果的官网上,我们已经可以下载到Xcode8的GM版本了,加上9.14日凌晨,苹果就要正式推出iOS10系统的推送了,在此之际,iOS10的适配已经迫在眉睫啦,不知道Xcode8 beat版本, ...

  2. mysql远程连接的设置

    有时数据库所在机器与项目运行的机器不是同一个,那么就涉及到远程链接数据库了,配置远程连接数据库的步骤如下: 1.查看mysql数据库中,user表中的信息,如下图,先使用use mysql切换到mys ...

  3. Redis入门简单操作

    一.下载安装 Linux下载服务器版本地址:https://redis.io/download Windows下载客户端Redis Desktop Manager:https://redisdeskt ...

  4. 利用 Create React Native App 快速创建 React Native 应用

    本文介绍的 Create-React-Native-App 是非常 Awesome 的工具,而其背后的 Expo 整个平台也让笔者感觉非常的不错.笔者目前公司是采用 APICloud 进行移动应用开发 ...

  5. Java第五次作业--面向对象高级特性(抽象类和接口)

    一.学习要点 认真看书并查阅相关资料,掌握以下内容: 掌握抽象类的设计 掌握接口的设计 理解简单工厂设计模式 理解抽象类和接口的区别 掌握包装类的应用 掌握对象的比较方法和比较器的使用 学习使用日期操 ...

  6. nginx 只容许域名访问禁止掉 ip 访问

    在原有 nginx server 的基础上再加上相同端口的配置 server {            listen 80 default_server;            server_name ...

  7. 未能加载文件或程序集“NPOI”或它的某一个依赖项

    自己遇到过得一个很麻瓜很耽误时间的bug,也请教了一些大神嫩是没找到解决方法 下面分享下问题和解决方法 做的是一个下载功能,本地是没问题IIS站点导出EXCEL的时候出错 我这边看不到错误信息,只能一 ...

  8. [TopCoder14647]HiddenRabbits

    vjudge description 有一棵\(n\)个节点的树和\(m\)只兔子,每只兔子要住在一个点上(可以多只兔子住在同一个点上).有\(q\)组要求,每组形如"当以\(r\)为根时, ...

  9. UEditor自定义toolbar工具条

    使用ueditor的同学都知道,ueditor里有很多功能,很全面,但有时候我们的编辑器不需要太多的功能,比如前台评论或者留言,就不需要这么多功能了,那我们怎么去定制自己想要的工具呢?官方给出了两个方 ...

  10. 什么是HBase(二) 关于HFile分割

    关于HFile的分割,是首先要从HFile的合并说起,上回书讲到memstore会不定期刷HFile,然后这些HFile将会被不定过期的被监控程序进行小合并+大合并(所有的文件,不分column fa ...