最终效果,是这样的,现在开始记录怎么做:

开始 npm 安装 wangEditor

安装好后,

因为要用vue 双向绑定 ,所以 我就把wangwangEditor 做成了一个封装组件,先看一下目录 :

我是把wangEditor写在了my-components这个项目下,新建一个 vue组件,代码如下:

  1. <template>
  2. <div id="wangeditor">
  3. <div ref="editorElem" style="text-align:left"></div>
  4. </div>
  5. </template>
  6. <script>
  7. import E from 'wangeditor'
  8. export default {
  9. name: 'editorElem',
  10. data() {
  11. return {
  12. editor: null,
  13. editorContent: ''
  14. }
  15. },
  16. props: ['catchData', 'content'], // 接收父组件的方法
  17. watch: {
  18. content() {
  19. this.editor.txt.html(this.content)
  20. }
  21. },
  22. mounted() {
  23. var imgUrl = "";
  24. this.editor = new E(this.$refs.editorElem)
  25.  
  26. this.editor.customConfig.onchange = (html) => {
  27. this.editorContent = html
  28. this.catchData(this.editorContent) // 把这个html通过catchData的方法传入父组件
  29. }
  30. this.editor.customConfig.uploadImgServer = '/api/Media/OnPostUpload'
  31. this.editor.customConfig.uploadVideoServer="/api/Media/OnPostUploadVideo" // 或 /node_modules/wangeditor/release/wangEditor.js 里直接写上传视频接口
  32. // 下面是最重要的的方法
  33. this.editor.customConfig.uploadImgHooks = {
  34. before: function (xhr, editor, files) {
  35. // 图片上传之前触发
  36. // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,files 是选择的图片文件
  37.  
  38. // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
  39. // return {
  40. // prevent: true,
  41. // msg: '放弃上传'
  42. // }
  43. },
  44. success: function (xhr, editor, result) {
  45. // 图片上传并返回结果,图片插入成功之后触发
  46. // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
  47.  
  48. this.imgUrl = Object.values(result.data).toString()
  49. },
  50. fail: function (xhr, editor, result) {
  51. debugger;
  52. var res = xhr.data;
  53. // 图片上传并返回结果,但图片插入错误时触发
  54. // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象,result 是服务器端返回的结果
  55. },
  56. error: function (xhr, editor) {
  57. debugger;
  58. // 图片上传出错时触发
  59. // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象
  60. },
  61. timeout: function (xhr, editor) {
  62. // 图片上传超时时触发
  63. // xhr 是 XMLHttpRequst 对象,editor 是编辑器对象
  64. },
  65.  
  66. // 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置
  67. // (但是,服务器端返回的必须是一个 JSON 格式字符串!!!否则会报错)
  68. customInsert: function (insertImg, result, editor) {
  69. // 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!)
  70. // insertImg 是插入图片的函数,editor 是编辑器对象,result 是服务器端返回的结果
  71.  
  72. // 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片:
  73. let url = Object.values(result.data) // result.data就是服务器返回的图片名字和链接
  74. JSON.stringify(url) // 在这里转成JSON格式
  75. insertImg(url)
  76. // result 必须是一个 JSON 格式字符串!!!否则报错
  77. },
  78. };
  79. this.editor.customConfig.debug = true;
  80. this.editor.create() // 创建富文本实例
  81. if (!this.content) {
  82. this.editor.txt.html('请编辑内容1')
  83. }
  84. }
  85. }
  86. </script>

  然后,再在主页面上,引用 ,和感觉和后端差不多

<template>
<editorElem :catchData="catchData" :content="channelForm.content"></editorElem> (:content 这里就是双向绑定)
</template>

import editorElem from "../../my-components/Editor.vue";

export default {

name: "editor",
   components: {
       editorElem
   }

}

接着,开始改wangEidotr的视频上传代码,原wangEidotr上传视频用的不怎么好,所以我就去网上找了大神的( https://blog.csdn.net/m0_37885651/article/details/83660206 )代码修改了一下,

先找到wangEidotr.js 

  1. //
  2. /*
  3. menu - video
  4. */
  5. // 构造函数
  6. function Video(editor) {
  7. this.editor = editor;
  8. this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-play"><i/></div>');
  9. this.type = 'panel';
  10.  
  11. // 当前是否 active 状态
  12. this._active = false;
  13. }
  14.  
  15. // 原型
  16. Video.prototype = {
  17.  
  18. constructor: Video,
  19.  
  20. onClick: function onClick() {
  21. this._createInsertPanel();
  22. },
  23.  
  24. _createInsertPanel: function _createInsertPanel() {
  25. var editor = this.editor;
  26. var uploadVideo = editor.uploadVideo;
  27. var config = editor.config;
  28.  
  29. // id
  30. var upTriggerId = getRandom('up-trigger');
  31. var upFileId = getRandom('up-file');
  32.  
  33. // tabs 的配置
  34. var tabsConfig = [{
  35. title: '上传 video',
  36. tpl: '<div class="w-e-up-img-container">\n ' +
  37. '<div id="' + upTriggerId + '" class="w-e-up-btn">\n ' +
  38. '<i class="w-e-icon-upload2"></i>\n </div>\n ' +
  39. '<div style="display:none;">\n <input id="' + upFileId + '" type="file" multiple="multiple" accept="audio/mp4, video/mp4"/>\n ' +
  40. '</div>\n </div>',
  41. events: [{
  42. // 触发选择视频
  43. selector: '#' + upTriggerId,
  44. type: 'click',
  45. fn: function fn() {
  46. var $file = $('#' + upFileId);
  47. var fileElem = $file[];
  48. if (fileElem) {
  49. fileElem.click();
  50. } else {
  51. // 返回 true 可关闭 panel
  52. return true;
  53. }
  54. }
  55. }, {
  56. // 选择视频完毕
  57. selector: '#' + upFileId,
  58. type: 'change',
  59. fn: function fn() {
  60. var $file = $('#' + upFileId);
  61. var fileElem = $file[];
  62. if (!fileElem) {
  63. // 返回 true 可关闭 panel
  64. return true;
  65. }
  66.  
  67. // 获取选中的 file 对象列表
  68. var fileList = fileElem.files;
  69. if (fileList.length) {
  70. uploadVideo.uploadVideo(fileList);
  71. }
  72.  
  73. // 返回 true 可关闭 panel
  74. return true;
  75. }
  76. }]
  77. }
  78. ]; // tabs end
  79.  
  80. // 判断 tabs 的显示
  81. var tabsConfigResult = [];
  82. tabsConfigResult.push(tabsConfig[]);
  83.  
  84. // 创建 panel 并显示
  85. var panel = new Panel(this, {
  86. width: ,
  87. tabs: tabsConfigResult
  88. });
  89. panel.show();
  90.  
  91. // 记录属性
  92. this.panel = panel;
  93. },
  94.  
  95. // 试图改变 active 状态
  96. tryChangeActive: function tryChangeActive(e) {
  97. var editor = this.editor;
  98. var $elem = this.$elem;
  99. if (editor._selectedImg) {
  100. this._active = true;
  101. $elem.addClass('w-e-active');
  102. } else {
  103. this._active = false;
  104. $elem.removeClass('w-e-active');
  105. }
  106. }
  107. };
  108.  
  109. /*
  110. 所有菜单的汇总
  111. */
  112.  
  113. // 存储菜单的构造函数
  114. var MenuConstructors = {};
  115.  
  116. MenuConstructors.video = Video;
  1. // 构造函数
  2. function UploadVideo(editor) {
  3. this.editor = editor;
  4. }
  5.  
  6. // 原型
  7. UploadVideo.prototype = {
  8. constructor: UploadVideo,
  9. // 根据 debug 弹出不同的信息
  10. _alert: function _alert(alertInfo, debugInfo) {
  11. var editor = this.editor;
  12. var debug = editor.config.debug;
  13. // var debug = true;
  14. var customAlert = editor.config.customAlert;
  15.  
  16. if (debug) {
  17. throw new Error('wangEditor: ' + (debugInfo || alertInfo));
  18. } else {
  19. if (customAlert && typeof customAlert === 'function') {
  20. customAlert(alertInfo);
  21. } else {
  22. alert(alertInfo);
  23. }
  24. }
  25. },
  26. //插入视频的方法 需要单独定义
  27. insertLinkVideo:function(link){
  28. var _this3 = this;
  29.  
  30. if (!link) {
  31. return;
  32. }
  33. var editor = this.editor;
  34. var config = editor.config;
  35.  
  36. // 校验格式
  37. var linkVideoCheck = config.linkVideoCheck;
  38. var checkResult = void ;
  39. if (linkVideoCheck && linkVideoCheck === 'function') {
  40. checkResult = linkVideoCheck(link);
  41. if (typeof checkResult === 'string') {
  42. // 校验失败,提示信息
  43. alert(checkResult);
  44. return;
  45. }
  46. }
  47.  
  48. editor.cmd.do('insertHTML', '<video src="' + link + '" style="width:50%;height: 50%;" controls autobuffer autoplay muted/>');
  49.  
  50. // 验证视频 url 是否有效,无效的话给出提示
  51. var video = document.createElement('video');
  52. video.onload = function () {
  53. var callback = config.linkVideoCallback;
  54. if (callback && typeof callback === 'function') {
  55. callback(link);
  56. }
  57.  
  58. video = null;
  59. };
  60. video.onerror = function () {
  61. video = null;
  62. // 无法成功下载图片
  63. _this2._alert('插入视频错误', 'wangEditor: \u63D2\u5165\u56FE\u7247\u51FA\u9519\uFF0C\u56FE\u7247\u94FE\u63A5\u662F "' + link + '"\uFF0C\u4E0B\u8F7D\u8BE5\u94FE\u63A5\u5931\u8D25');
  64. return;
  65. };
  66. video.onabort = function () {
  67. video = null;
  68. };
  69. video.src = link;
  70. },
  71. // 上传视频
  72. uploadVideo: function uploadVideo(files) {
  73. var _this3 = this;
  74.  
  75. if (!files || !files.length) {
  76. return;
  77. }
  78.  
  79. // ------------------------------ 获取配置信息 ------------------------------
  80. var editor = this.editor;
  81. var config = editor.config;
  82. var uploadVideoServer = "/video/uploadVideo";//上传地址
  83.  
  84. var maxSize = * * ; //100M
  85. var maxSizeM = maxSize / / ;
  86. var maxLength = ;
  87. var uploadFileName = "file";
  88. var uploadVideoParams = config.uploadVideoParams || {};
  89. var uploadVideoHeaders = {};
  90. var hooks =config.uploadImgHooks || {};
  91. var timeout = * * ; //5 min
  92. var withCredentials = config.withCredentials;
  93. if (withCredentials == null) {
  94. withCredentials = false;
  95. }
  96.  
  97. // ------------------------------ 验证文件信息 ------------------------------
  98. var resultFiles = [];
  99. var errInfo = [];
  100. arrForEach(files, function (file) {
  101. var name = file.name;
  102. var size = file.size;
  103.  
  104. // chrome 低版本 name === undefined
  105. if (!name || !size) {
  106. return;
  107. }
  108.  
  109. if (/\.(mp4)$/i.test(name) === false) {
  110. // 后缀名不合法,不是视频
  111. errInfo.push('\u3010' + name + '\u3011\u4e0d\u662f\u89c6\u9891');
  112. return;
  113. }
  114. if (maxSize < size) {
  115. // 上传视频过大
  116. errInfo.push('\u3010' + name + '\u3011\u5927\u4E8E ' + maxSizeM + 'M');
  117. return;
  118. }
  119.  
  120. // 验证通过的加入结果列表
  121. resultFiles.push(file);
  122. });
  123. // 抛出验证信息
  124. if (errInfo.length) {
  125. this._alert('视频验证未通过: \n' + errInfo.join('\n'));
  126. return;
  127. }
  128. if (resultFiles.length > maxLength) {
  129. this._alert('一次最多上传' + maxLength + '个视频');
  130. return;
  131. }
  132.  
  133. // ------------------------------ 自定义上传 ------------------------------
  134. // 添加视频数据
  135. var formdata = new FormData();
  136. arrForEach(resultFiles, function (file) {
  137. var name = uploadFileName || file.name;
  138. formdata.append(name, file);
  139. });
  140.  
  141. // ------------------------------ 上传视频 ------------------------------
  142. if (uploadVideoServer && typeof uploadVideoServer === 'string') {
  143. // 添加参数
  144. var uploadVideoServer = uploadVideoServer.split('#');
  145. uploadVideoServer = uploadVideoServer[];
  146. var uploadVideoServerHash = uploadVideoServer[] || '';
  147. objForEach(uploadVideoParams, function (key, val) {
  148. val = encodeURIComponent(val);
  149.  
  150. // 第一,将参数拼接到 url 中
  151. if (uploadVideoParamsWithUrl) {
  152. if (uploadVideoServer.indexOf('?') > ) {
  153. uploadVideoServer += '&';
  154. } else {
  155. uploadVideoServer += '?';
  156. }
  157. uploadVideoServer = uploadVideoServer + key + '=' + val;
  158. }
  159.  
  160. // 第二,将参数添加到 formdata 中
  161. formdata.append(key, val);
  162. });
  163. if (uploadVideoServerHash) {
  164. uploadVideoServer += '#' + uploadVideoServerHash;
  165. }
  166.  
  167. // 定义 xhr
  168. var xhr = new XMLHttpRequest();
  169. xhr.open('POST', uploadVideoServer);
  170.  
  171. // 设置超时
  172. xhr.timeout = timeout;
  173. xhr.ontimeout = function () {
  174. // hook - timeout
  175. if (hooks.timeout && typeof hooks.timeout === 'function') {
  176. hooks.timeout(xhr, editor);
  177. }
  178.  
  179. _this3._alert('上传视频超时');
  180. };
  181.  
  182. // 监控 progress
  183. if (xhr.upload) {
  184. xhr.upload.onprogress = function (e) {
  185. var percent = void ;
  186. // 进度条
  187. var progressBar = new Progress(editor);
  188. if (e.lengthComputable) {
  189. percent = e.loaded / e.total;
  190. progressBar.show(percent);
  191. }
  192. };
  193. }
  194.  
  195. // 返回数据
  196. xhr.onreadystatechange = function () {
  197. var result = void ;
  198. if (xhr.readyState === ) {
  199. if (xhr.status < || xhr.status >= ) {
  200. // hook - error
  201. if (hooks.error && typeof hooks.error === 'function') {
  202. hooks.error(xhr, editor);
  203. }
  204.  
  205. // xhr 返回状态错误
  206. _this3._alert('上传视频发生错误', '\u4E0A\u4F20\u56FE\u7247\u53D1\u751F\u9519\u8BEF\uFF0C\u670D\u52A1\u5668\u8FD4\u56DE\u72B6\u6001\u662F ' + xhr.status);
  207. return;
  208. }
  209.  
  210. result = xhr.responseText;
  211. if ((typeof result === 'undefined' ? 'undefined' : _typeof(result)) !== 'object') {
  212. try {
  213. console.log(result);
  214. result = JSON.parse(result);
  215. } catch (ex) {
  216. // hook - fail
  217. if (hooks.fail && typeof hooks.fail === 'function') {
  218. hooks.fail(xhr, editor, result);
  219. }
  220. _this3._alert('上传视频失败', '上传视频返回结果错误,返回结果是: ' + result);
  221. return;
  222. }
  223. }
  224. if (!hooks.customInsert && result.errno == '') {
  225. // hook - fail
  226. if (hooks.fail && typeof hooks.fail === 'function') {
  227. hooks.fail(xhr, editor, result);
  228. }
  229.  
  230. // 数据错误
  231. _this3._alert('上传视频失败', '上传视频返回结果错误,返回结果 errno=' + result.errno);
  232. } else {
  233. if (hooks.customInsert && typeof hooks.customInsert === 'function') {
  234. hooks.customInsert(_this3.insertLinkVideo.bind(_this3), result, editor);
  235. } else {
  236. // 将视频插入编辑器
  237. var data = result || [];
  238. // data.forEach(function (link) {
  239. // console.log(link);
  240. //
  241. // });
  242. _this3.insertLinkVideo(data.url);
  243. }
  244.  
  245. // hook - success
  246. if (hooks.success && typeof hooks.success === 'function') {
  247. hooks.success(xhr, editor, result);
  248. }
  249. }
  250. }
  251. };
  252.  
  253. // hook - before
  254. if (hooks.before && typeof hooks.before === 'function') {
  255. var beforeResult = hooks.before(xhr, editor, resultFiles);
  256. if (beforeResult && (typeof beforeResult === 'undefined' ? 'undefined' : _typeof(beforeResult)) === 'object') {
  257. if (beforeResult.prevent) {
  258. // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
  259. this._alert(beforeResult.msg);
  260. return;
  261. }
  262. }
  263. }
  264.  
  265. // 自定义 headers
  266. objForEach(uploadVideoHeaders, function (key, val) {
  267. xhr.setRequestHeader(key, val);
  268. });
  269.  
  270. // 跨域传 cookie
  271. xhr.withCredentials = withCredentials;
  272.  
  273. // 发送请求
  274. xhr.send(formdata);
  275.  
  276. // 注意,要 return 。不去操作接下来的 base64 显示方式
  277. return;
  278. }
  279. }
  280. };
  1. // 修改原型
  2. Editor.prototype = {
  3. constructor: Editor,
  4.  
  5. // 添加视频上传
  6. _initUploadVideo: function _initUploadVideo() {
  7. this.uploadVideo = new UploadVideo(this);
  8. },
  9. // 创建编辑器
  10. create: function create() {
  11.  
  12. // 添加 视频上传
  13. this._initUploadVideo();
  14. },
  15.  
  16. };

1,源码里找到Video 的构造函数 还有它的prototype,替换成第一部分。(有版本是一样的,可以不替换)
2,找到UploadImg的构造函数,还有它的prototype,这是上传图片的,相应的 你在这后面 加上第二部分UploadVideo的构造和原型,这是专门写的上传视频的。
3,在Editor原型上加个方法,_initUploadVideo , 然后在 创建编辑器 create 里面执行 this._initUploadVideo() ,加上就可以。

下面是后端代码:

  1. #region 上传图片 OnPostUpload
  2. [HttpPost]
  3. public async Task<IActionResult> OnPostUpload([FromServices]IHostingEnvironment environment)
  4. {
  5. List<string> fileUrl = new List<string>();
  6. var files = Request.Form.Files;
  7. if (string.IsNullOrWhiteSpace(environment.WebRootPath))
  8. {
  9. environment.WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
  10. }
  11.  
  12. string webRootPath = environment.WebRootPath;
  13. string filePath = Path.Combine(webRootPath + "\\upload\\images");
  14. if (!Directory.Exists(filePath))
  15. {
  16. Directory.CreateDirectory(filePath);
  17. }
  18.  
  19. foreach (var formFile in files)
  20. {
  21. if (formFile.Length > )
  22. {
  23. var ext = Path.GetExtension(formFile.FileName);
  24. if (!pictureFormatArray.Contains(ext.Split('.')[]))
  25. {
  26. return new JsonResult(TmpUrl.ErrorInfo("图片格式不正确!", null));
  27. }
  28. var fileName = Guid.NewGuid().ToString() + ext;
  29. var path = Path.Combine(webRootPath + "\\upload\\images", fileName);
  30. using (var stream = new FileStream(path, FileMode.CreateNew))
  31. {
  32. await formFile.CopyToAsync(stream);
  33. fileUrl.Add($"/api/Media/ShowNoticeImg?filePath={fileName}");
  34. }
  35. }
  36. }
  37.  
  38. return new JsonResult(TmpUrl.SuccessInfo("ok!", fileUrl));
  39. }
  40. #endregion
  41.  
  42. #region 上传视频 OnPostUploadVideo
  43. [HttpPost]
  44. public async Task<IActionResult> OnPostUploadVideo([FromServices]IHostingEnvironment environment)
  45. {
  46. List<string> fileUrl = new List<string>();
  47. var files = Request.Form.Files;
  48. if (string.IsNullOrWhiteSpace(environment.WebRootPath))
  49. {
  50. environment.WebRootPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
  51. }
  52.  
  53. string webRootPath = environment.WebRootPath;
  54. string filePath = Path.Combine(webRootPath + "\\upload\\videos");
  55. if (!Directory.Exists(filePath))
  56. {
  57. Directory.CreateDirectory(filePath);
  58. }
  59.  
  60. foreach (var formFile in files)
  61. {
  62. if (formFile.Length > )
  63. {
  64. var ext = Path.GetExtension(formFile.FileName);
  65. if (!videoFormatArray.Contains(ext.Split('.')[]))
  66. {
  67. return new JsonResult(TmpUrl.ErrorInfo("视频格式不正确!", null));
  68. }
  69. var fileName = Guid.NewGuid().ToString() + ext;
  70. var path = Path.Combine(webRootPath + "\\upload\\videos", fileName);
  71. using (var stream = new FileStream(path, FileMode.CreateNew))
  72. {
  73. await formFile.CopyToAsync(stream);
  74. //fileUrl.Add($"/upload/videos/{fileName}");
  75. fileUrl.Add("http://localhost:15429/upload/videos/8e11ae8e-8ecc-4b7c-afac-43601530493f.mp4");
  76. }
  77. }
  78. }
  79.  
  80. return new JsonResult(TmpUrl.SuccessInfo("ok!", fileUrl));
  81. }
  82. #endregion
  83.  
  84. #region 获取图片流 ShowNoticeImg
  85. public IActionResult ShowNoticeImg(string filePath)
  86. {
  87. var contentTypeStr = "image/jpeg";
  88. string webRootPath = Path.Combine(Directory.GetCurrentDirectory(), $"wwwroot\\upload\\images\\{filePath}");
  89. using (var sw = new FileStream(webRootPath, FileMode.Open))
  90. {
  91. var bytes = new byte[sw.Length];
  92. sw.Read(bytes, , bytes.Length);
  93. sw.Close();
  94. return new FileContentResult(bytes, contentTypeStr);
  95. }
  96. }
  97. #endregion

.net core vue+wangEditor (双向绑定) 上传图片和视频功能的更多相关文章

  1. Vue.js双向绑定的实现原理

    Vue.js最核心的功能有两个,一是响应式的数据绑定系统,二是组件系统.本文仅探究几乎所有Vue的开篇介绍都会提到的hello world双向绑定是怎样实现的.先讲涉及的知识点,再参考源码,用尽可能少 ...

  2. Vue.js双向绑定的实现原理和模板引擎实现原理(##########################################)

    Vue.js双向绑定的实现原理 解析 神奇的 Object.defineProperty 这个方法了不起啊..vue.js和avalon.js 都是通过它实现双向绑定的..而且Object.obser ...

  3. vue的双向绑定原理及实现

    前言 使用vue也好有一段时间了,虽然对其双向绑定原理也有了解个大概,但也没好好探究下其原理实现,所以这次特意花了几晚时间查阅资料和阅读相关源码,自己也实现一个简单版vue的双向绑定版本,先上个成果图 ...

  4. 西安电话面试:谈谈Vue数据双向绑定原理,看看你的回答能打几分

    最近我参加了一次来自西安的电话面试(第二轮,技术面),是大厂还是小作坊我在这里按下不表,先来说说这次电面给我留下印象较深的几道面试题,这次先来谈谈Vue的数据双向绑定原理. 情景再现: 当我手机铃声响 ...

  5. Vue数据双向绑定原理及简单实现

    嘿,Goodgirl and GoodBoy,点进来了就看完点个赞再go. Vue这个框架就不简单介绍了,它最大的特性就是数据的双向绑定以及虚拟dom.核心就是用数据来驱动视图层的改变.先看一段代码. ...

  6. vue数据双向绑定

    Vue的双向绑定是通过数据劫持结合发布-订阅者模式实现的,即通过Object.defineProperty监听各个属性的setter,然后通知订阅者属性发生变化,触发相应的回调. 整个过程分为以下几步 ...

  7. 【学习笔记】剖析MVVM框架,简单实现Vue数据双向绑定

    前言: 学习前端也有半年多了,个人的学习欲望还比较强烈,很喜欢那种新知识在自己的演练下一点点实现的过程.最近一直在学vue框架,像网上大佬说的,入门容易深究难.不管是跟着开发文档学还是视频教程,按步骤 ...

  8. vue数据双向绑定原理

    vue的数据双向绑定的小例子: .html <!DOCTYPE html> <html> <head> <meta charset=utf-> < ...

  9. Vue.js双向绑定原理

    Vue.js最核心的功能有两个,一个是响应式的数据绑定系统,另一个是组件系统.本文仅仅探究双向绑定是怎样实现的.先讲涉及的知识点,再用简化的代码实现一个简单的hello world示例. 一.访问器属 ...

随机推荐

  1. Qt压缩和解压 zip

    zlib编译详见 https://blog.csdn.net/zhangxuechao_/article/details/85049711 下载quazip https://github.com/st ...

  2. centos7 ntp server & samba

    最近公司内部一个需求:必须 Linux建个 ntp server ,并且 Windows可以net time \\ip 访问. 想要解决问题,还得解决前置问题. 服务器不能上网,无法直接访问外部 yu ...

  3. React中ref的三种用法 可以用来获取表单中的值 这一种类似document.getXXId的方式

    import React, { Component } from "react" export default class MyInput extends Component { ...

  4. unsigned int数据类型最大数

    #include <stdio.h> int main() { unsigned , b = ; ) { a++; } printf( ); unsigned ; do { n = n / ...

  5. 201871010101- 陈来弟《面向对象程序设计(java)》第6-7周学习总结

    201871010101- 陈来弟<面向对象程序设计(java)>第6-7周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh ...

  6. 第七周scrum会议

    本周会议地点依旧 会议照片: 本周内容: 讨论了进行中的难点 我们正在分析图书馆首页的网页结构以及各种跳转的请求以及链接,为爬虫的实现奠定基础. flask框架我们也遇到了很多问题,正在进行官方文档的 ...

  7. django之三剑客、静态文件配置、请求响应对象、数据库操作

    三剑客 from django.shortcuts import render,HttpResponse,redirect HttpResponse # 返回字符串 render(response, ...

  8. JavaScript中的this—你不知道的JavaScript上卷读书笔记(三)

    this是什么? this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件.this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式.当一个函数被调用时,会 ...

  9. python之路—从入门到放弃

    python基础部分 函数 初识函数 函数进阶 装饰器函数 迭代器和生成器 内置函数和匿名函数 递归函数 常用模块 常用模块 模块和包 面向对象 初识面向对象 面向对象进阶 网络编程 网络编程 并发编 ...

  10. Scrapy框架详解

    Python网络爬虫Scrapy框架研究 Scrapy1.0教程 Scrapy笔记(1)- 入门篇 Scrapy笔记(2)- 完整示例 Scrapy笔记(3)- Spider详解 Scrapy笔记(4 ...