单个vue文件怎么运行,vue表单数据和文件一起提交

  单个vue文件怎么运行,vue表单数据和文件一起提交

  整个项目结构清晰,尤其是单文件组件的表现力特别突出,使得各个组件的逻辑不会太复杂。所以本文主要介绍vue实现一个单文件组件的相关信息,有需要的朋友可以参考一下。

  

目录

  前言文档组件

  基本概念

  简单装载机

  解析组件内容

  注册组件

  获取脚本内容

  数据URI和对象URI

  动态导入

  实现

  行为层

  兼容性问题和其他问题

  摘要

  

前言

  前端开发人员只要了解过vue.js框架,就可能知道单文件组件。vue.js中的单文件组件允许在一个文件中定义组件的所有内容。这是一个非常有用的解决方案,这种机制已经在浏览器网页中得到提倡。但遗憾的是,这个概念从2017年8月提出至今,没有任何进展,好像要消亡了。不过深入研究这个话题,尝试利用现有技术实现单文件组件,还是很有意思的,值得一试。

  

单文件组件

  知道“逐步增强”这个概念的前端开发者,一定也听说过“分层”这个概念。在组件中,也有这样一个概念。事实上,每个组件至少有3层,甚至3层以上:内容/模板、表现和行为。或者保守一点,每个组件至少会分成三档。例如,按钮组件的文件结构可能如下所示:

  按钮/

  -Button.html

   - Button.css

   - Button.js

  这样分层相当于技术的分离(内容/模板:使用html,性能:使用css,行为:使用JavaScript)。如果不使用任何构建工具进行打包,就意味着浏览器需要获取这3个文件。因此,一个想法是迫切需要一种分离组件代码而不分离技术(文件)的技术来解决这个问题。这就是本文的主题——单个文件组件。

  总的来说,我对“技术分层”持怀疑态度。它来源于组件分层往往因为绕不开“技术分层”而被抛弃,两者完全割裂。

  回到主题,用单文件组件实现按钮可能如下所示:

  模板

  !-Button.html目录在这里。-

  /模板

  风格

  /* Button.css内容放在这里。*/

  /风格

  脚本

  //Button.js内容放在这里。

  /脚本

  可以看出,这个单文件组件非常类似于原来前端开发中的html文档。它有自己的样式标签和脚本标签,但是表示层使用模板标签。由于这种简单的方式,一个强大的分层组件(内容/模板:模板,性能:样式,行为:脚本)无需使用三个单独的文件就可以获得。

  

基本概念

  首先,我们创建一个全局函数loadComponent()来加载组件。

  window . load component=(function(){

  函数加载组件(URL ) {}

  返回loadComponent

  }());

  这里使用的是JavaScript模块模式。它允许定义所有必要的辅助函数,但只公开loadComponent()函数。当然这个功能现在还是空的。

  稍后,我们将创建一个hello-world组件来显式显示以下内容。

  你好,世界!我的名字是教名。

  另外,点击这个组件,会弹出一条消息:

  别碰我!

  代码保存为文件HelloWorld.wc(其中。wc代表Web组件)。初始代码如下:

  模板

  你好

  费洛,世界!我的名字是插槽/插槽。/p

  /div

  /模板

  风格

  部门{

  背景:红色;

  边框半径:30px

  填充:20px

  字体大小:20px

  文本对齐:居中;

  宽度:300px

  边距:0自动;

  }

  /风格

  脚本/脚本

  目前,组件中没有添加任何行为,只定义了模板和样式。模板,可以使用常见的html标签,比如div。另外,模板中的slot元素表示组件将实现shadow DOM。并且默认情况下,这个DOM本身的所有样式和模板都隐藏在这个DOM中。

  组件在网页中的使用方式非常简单。

  你好-世界指挥官/你好-世界

  script src=loader.js/script

  脚本

  load component( hello world . WC );

  /脚本

  您可以使用像标准定制元素这样的组件。唯一的区别是,在使用loadComponent()方法之前,需要先加载它(这个方法放在loader.js中)。load()方法完成所有繁重的工作,比如获取组件并通过customElements.define()注册它。

  知道了所有的概念之后,就该练习了。

  

简单的loader

  如果要从外部文件加载文件,就需要使用万能的ajax。但现在是2020年。在大多数浏览器中,可以大胆使用Fetch API。

  函数加载组件(URL ) {

  返回fetch(URL);

  }

  但是,这只是获取文件,没有对文件进行任何处理。接下来要做的是将ajax返回的内容转换成文本文本文本,如下所示:

  函数加载组件(URL ) {

  返回获取(URL)。然后( (响应)={

  返回response . text();

  } );

  }

  loadComponent()函数是一个Promise对象,因为它返回fetch函数的执行结果。您可以检查文件(HelloWorld.wc)是否实际加载到then方法中,以及它是否已经转换为文本:

  运行结果如下:

  在chrome浏览器下,使用console()方法,我们看到HelloWorld.wc的内容被转换成文本并输出,看起来是可以工作的!

  

解析组件内容

  然而,仅仅输出文本并没有达到我们的目的。最后要转换成DOM进行展示,并能真正与用户互动。

  浏览器环境中有一个非常实用的类DOMParser,可以用来创建一个DOM解析器。实例化一个DOMParser类以获得一个对象,该对象可用于将组件文本转换为DOM:

  window . load component=(function(){

  函数加载组件(URL) {

  返回获取(URL)。然后((响应)={

  返回response . text();

  }).然后((html)={

  const parser=new DOM parser();//1

  return parser . parsefromstring(html, text/html );//2

  });

  }

  返回loadComponent

  }());

  首先,创建一个DOMParser实例parser(1),然后使用这个实例将组件内容转换成DOM(2)。值得注意的是,这里实用的是HTML模式( text/html )。如果您希望代码更好地符合JSX标准或原始Vue.js组件,您可以应用XML模式( text/XML )。但是,在这种情况下,组件本身的结构需要改变(例如,添加一个可以容纳其他元素的主元素)。

  这是再次输出loadComponent()函数的结果,它是一棵DOM树。

  在chrome浏览器下,console.log()输出解析后的HelloWorld.wc文件,这是一个DOM树。

  请注意,parser.parseFromString方法会自动向组件添加html、head和body标记元素。这是HTML解析器的工作原理造成的。HTML LS规范中详细描述了构建DOM树的算法。这篇文章很长。阅读需要一些时间。可以简单理解为,解析器默认会把所有东西都放在head元素里,直到遇到一个只能放在body标签里的DOM元素。因此,组件代码中的所有元素(元素、样式、脚本)都允许放在头部。如果一个P元素被包装在模板之外,解析器会把它放在主体中。

  还有一个问题。组件解析后,并没有!DOCTYPE html语句,所以这得到了一个异常的html文档,所以浏览器会使用一种叫做怪异模式的方式来渲染这个html文档。幸运的是,这里不会带来任何负面影响,因为这里只使用了DOM parser将组件分成适当的部分。

  有了DOM树,我们可以只截取我们需要的部分。

  返回获取(URL)。然后((响应)={

  返回response . text();

  }).然后((html)={

  const parser=new DOM parser();

  const document=parser . parsefromstring(html, text/html );

  const head=document.head

  const template=head . query selector( template );

  const style=head . query selector( style );

  const script=head . query selector( script );

  返回{

  模板,

  风格,

  脚本

  };

  });

  最后整理一下代码。loadComponent方法如下。

  window . load component=(function(){

  函数fetchAndParse(URL) {

  返回获取(URL)。然后((响应)={

  返回response . text();

  }).然后((html)={

  const parser=new DOM parser();

  const document=parser . parsefromstring(html, text/html );

  const head=document.head

  const template=head . query selector( template );

  const style=head . query selector( style );

  const script=head . query selector( script );

  返回{

  模板,

  风格,

  脚本

  };

  });

  }

  函数加载组件(URL) {

  返回fetchAndParse(URL);

  }

  返回loadComponent

  }());

  Fetch API并不是从外部文件获取组件代码的唯一方式。XMLHttpRequest有一个专用的文档模式,允许您省略整个解析步骤。但是XMLHttpRequest返回的不是承诺。这个需要自己包。

  

注册组件

  现在已经有了组件层,可以创建registerComponent()方法来注册一个新的定制组件。

  window . load component=(function(){

  函数fetchAndParse(URL) {

  […]

  }

  函数注册组件(){

  }

  函数加载组件(URL) {

  返回fetchAndParse(URL)。then(register component);

  }

  返回loadComponent

  }());

  请注意,自定义组件必须是从HTMLElement继承的类。此外,每个组件都将使用一个影子DOM来存储样式和模板内容。所以每次引用这个组件,这个组件的样式都是一样的。该方法如下:

  函数registerComponent({template,style,script}) {

  类UnityComponent扩展HTMLElement {

  connectedCallback() {

  这个。_ upcast();

  }

  _upcast() {

  const shadow=this . attach shadow({ mode: open });

  shadow . appendchild(style . clone node(true));

  shadow . appendchild(document . import node(template . content,true));

  }

  }

  }

  UnityComponent类应在registerComponent()方法中创建,因为该类使用传递给registerComponent()的参数。这个类将使用稍微修改过的机制来实现影子DOM,我在这篇关于影子DOM(波兰语)的文章中已经详细介绍过了。

  关于注册组件只剩下一件事了。给单文件组件命名,并将其添加到当前页面的DOM中。

  函数registerComponent( { template,style,script } ) {

  类UnityComponent扩展HTMLElement {

  [.]

  }

  返回custom elements . define( hello-world ,unity component);

  }

  现在可以打开看看了,如下图:

  在chrome中,在这个按钮组件中,有一个红色的矩形,上面写着:Hello,world!我叫科曼迪尔.

  

获取脚本内容

  现在已经实现了一个简单的按钮组件。现在来实现最难的部分,添加一个行为层,自定义按钮里面的内容。在上面的步骤中,我们应该使用按钮传入的地方,而不是在组件代码中硬编码按钮内部的文本内容。同样,我们必须处理绑定在组件中的事件监控。这里,我们使用类似于Vue.js的约定,如下所示:

  模板

  […]

  /模板

  风格

  […]

  /风格

  脚本

  导出默认值{ //1

  名称: hello-world ,//2

  onClick() { //3

  警惕(`别碰我!` );

  }

  }

  /脚本

  可以假设组件中script标签中的内容是一个JavaScript模块,它导出内容(1)。模块的导出对象包含组件的名称(2)和以“on .”开头的事件监听方法(3)。

  这看起来很整洁,没有任何东西暴露在模块之外(因为JavaScript中的模块不在全局范围内)。这里有一个问题:没有一个标准可以处理从内部模块导出的对象(这些代码直接在HTML文档中定义)。import语句将假设获得了一个模块ID,并根据这个ID进行导入。最常见的是来自包含代码的文件的URL路径。组件不是js文件,没有这样的标识,内部模块也没有这样的标识。

  在投降之前,你可以用一个超级脏的黑客。浏览器至少有两种方法来处理像文件一样的文本:数据URI和对象URI。也有一些使用服务人员的建议。但在这里似乎有点大材小用。

  

Data URI和Object URI

  数据URI是一种古老而原始的方法。它的基础是将文件内容转换成URL,去掉不必要的空格,然后用Base64对所有内容进行编码。假设有一个包含以下内容的JavaScript文件:

  导出默认值true

  转换为数据URI,如下所示:

  数据:应用/JavaScript;base64,ZXhwb3J0IGRlZmF1bHQgdHJ1ZTs=

  然后,这个URI可以像一个文件一样被介绍:

  从“数据:应用程序/javascript”导入测试;base64,zxhwb 3j 0 igrlzm f1 bhqgdhj 1 zts=;

  console.log(测试);

  URI的一个明显的缺点是,随着JavaScript文件内容的增加,这个URL的长度会变得很长。将二进制数据放入数据URI非常困难。

  所以,现在有了一个新的对象URI。它源自几个标准,包括文件API和HTML5中的视频和音频标签。对象URI的目的很简单,从给定的二进制数据创建一个“伪文件”,并在当前上下文中给出一个唯一的URI。简单来说就是在内存中创建一个唯一名称的文件。对象URI具有数据URI(一种创建“文件”的方法)的所有优点,而没有它的缺点(即使文件有100M长)。

  对象URIs通常是从多媒体流(例如,在视频或音频环境中)或通过input [type=file]和拖放机制发送的文件中创建的。您也可以使用两个类File和Blob来手动创建它。在本例中,我们使用Bolb,首先将内容放入模块,然后将其转换为对象URI:

  const myJSFile=new Blob([ export default true;],{ type: application/JavaScript });

  const myJSURL=URL . createobjecturl(myJSFile);

  console . log(myJSURL);//blob:https://blog . comandeer . pl/8e 8 FBD 73-5505-470d-a797-DFB 06 ca 71333

  

动态导入

  但是,还有一个问题:import语句不接受变量作为模块标识符。这意味着除了使用此方法将模块转换为“文件”之外,不能导入模块。还是无解?

  不完全是。这个问题很久以前就提出来了,使用动态导入机制就可以解决。它是ES2020标准的一部分,已在Firefox、Safari和Node.js13.x中实现。使用变量作为要动态导入的模块的标识符不再是问题:

  const myJSFile=new Blob([ export default true;],{ type: application/JavaScript });

  const myJSURL=URL . createobjecturl(myJSFile);

  导入(myJSURL)。然后( (模块)={

  console . log(module . default);//真

  });

  从上面的代码可以看出,import()命令可以像方法一样使用。它返回一个Promise对象,模块对象是在then方法中获得的。它的默认属性包含模块中定义的所有导出对象。

  

实现

  既然我们知道了这个想法,我们就可以开始实现它了。添加一个工具方法getSetting()。在registerComponents()方法之前调用它,从脚本代码中获取所有信息。

  函数getSettings( { template,style,script } ) {

  返回{

  模板,

  风格,

  脚本

  };

  }

  [.]

  函数加载组件(URL ) {

  返回fetchAndParse( URL)。然后(获取设置)。then(register component);

  }

  现在,这个方法返回所有传入的参数。根据上面介绍的逻辑,将脚本代码转换成对象URI:

  const jsFile=new Blob([script . text content],{ type: application/JavaScript });

  const jsURL=URL . createobjecturl(jsFile);

  接下来,使用import加载模块并返回模板、样式和组件的名称:

  返回导入(jsURL)。然后( (模块)={

  返回{

  名称:module.default.name,

  模板,

  风格

  }

  } );

  出于这个原因,registerComponent()仍然获得3个参数,但现在它获得的是name而不是script。正确的代码如下:

  函数registerComponent( { template,style,name } ) {

  类UnityComponent扩展HTMLElement {

  [.]

  }

  返回customElements.define( name,unity component);

  }

  

行为层

  组件中还剩下最后一层:行为层,用于处理事件。现在,我们只需在getSettings()方法中获取组件的名称,并获取事件监控。您可以使用Object.entrie()方法获得它。在getSettings()方法中添加适当的代码:

  函数getSettings( { template,style,script } ) {

  [.]

  函数getListeners( settings ) { //1

  const listeners={ };

  对象.条目(设置)。forEach( ( [设置,值] )={ //3

  if(setting . starts with( on ){//4

  侦听器[设置[ 2 ]。toLowerCase()setting . substr(3)]=value;//5

  }

  } );

  返回侦听器;

  }

  返回导入(jsURL)。然后( (模块)={

  const listeners=get listeners(module . default);//2

  返回{

  名称:module.default.name,

  侦听器,//6

  模板,

  风格

  }

  } );

  }

  现在方法变得有点复杂了。添加了一个新函数getListeners()(1)来将模块的输出传递给这个参数。

  然后使用Object.entries()(3)方法遍历导出的模块。如果当前属性以“on”(4)开头,则它是一个侦听器函数。将该节点的值(listener函数)添加到listeners对象,并使用设置[2]。to lower case()setting . substr(3)(5)获取键值。

  键值是通过去掉开头的“on”并将后面的“click”的首字母转换成小写(即从onClick中获取Click作为构建值)而形成的。然后传入isteners对象(6)。

  您可以使用[]。reduce()方法而不是[]。forEach()方法,以便可以省略侦听器的变量,如下所示:

  函数getListeners(设置){

  返回Object.entries(设置)。reduce( ( listeners,[ setting,value ] )={

  if(setting . starts with( on ){

  侦听器[设置[ 2 ]。toLowerCase()setting . substr(3)]=value;

  }

  返回侦听器;

  }, {} );

  }

  现在,您可以将监视绑定到组件内部的类中:

  function register component({ template,style,name,listeners } ) { //1

  类UnityComponent扩展HTMLElement {

  connectedCallback() {

  这个。_ upcast();

  这个。_ attach listeners();//2

  }

  [.]

  _attachListeners() {

  对象.条目(侦听器)。forEach( ( [事件,侦听器] )={ //3

  this.addEventListener( event,Listener,false);//4

  } );

  }

  }

  返回customElements.define( name,unity component);

  }

  listeners方法(1)中添加了一个参数,该类中添加了一个新的method _attachListeners()(2)。在这里,您可以再次使用Object.entries()来遍历侦听器(3)并将它们绑定到元素(4)。

  最后点击组件弹出“别碰我!”,如下所示:

  

兼容性问题及其他

  可以看到,为了实现这个单文件组件,大部分工作都集中在如何支持基本表单上。他们中的许多人使用肮脏的手段(在es中使用对象URI加载模块,没有浏览器的支持,这项技术毫无意义)。幸运的是,所有技术在主流浏览器中都运行良好,包括Chrome、Firefox和Safari。

  尽管如此,创建这样一个项目还是很有趣的,它将接触到许多浏览器技术和最新的web标准。

  最后,你可以在网上得到这个项目的代码。

  

总结

  这就是这篇关于vue实现单个文件组件的文章。有关vue单文件组件的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望大家以后能多多支持我们!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: