物勒工名,以考其诚

关键词搜索结果:

Web前端之家:一起探讨Vue Router的神奇之旅

Web前端之家今天介绍下VueRouter的基本知识,一起探讨VueRouter的神奇之旅。我们将研究如何使用VueRouter在Vue应用程序中实现路由。所以我们可以动手实践,我们将使用Vue和VueRouter构建一个简单的Pokedex应用程序。具体来说,我们将涵盖以下内容:设置路由器路由参数声明式和程序化导航嵌套路由404页每个允许创建单页应用程序的JavaScriptUI框架都需要一种将用户从一个页面导航到另一个页面的方法。所有这一切都需要在客户端通过将页面上当前显示的视图与地址栏中的URL同步来进行管理。在Vue世界中,管理此类任务的【官方库】是VueRouter。先决条件以下是必需的,以便您可以充分利用本教程:HTML、CSS、JavaScript和Vue的基本知识。如果您知道如何使用Vue在页面上渲染某些内容,那么您应该能够跟上。对API有一点了解也会有所帮助。Node.js和VueCLI安装在您的机器上。我们将在本教程中使用Vue3,因此请确保VueCLI已更新。应用概览我们将构建一个Pokedex应用程序。它将包含三个页面:口袋妖怪列表页面。这是列出所有原始151Pokemon的默认页面。口袋妖怪页面。这是我们显示基本详细信息的地方,例如类型和描述。口袋妖怪详情页。这是我们展示进化链、能力和动作的地方。设置应用程序使用VueCLI启动一个新的Vue应用程序:vue create poke-vue-router从列出的选项中选择Vue3:完成后,在项目文件夹中导航并安装我们需要的库:cd poke-vue-routernpm install vue-router@4 axios请注意,我们使用的是VueRouter4而不是3,这是您在Google上搜索时显示的默认结果。它next.router.vuejs.org与router.vuejs.org. 我们正在使用Axios向PokeAPIv2发出请求。此时,最好运行项目以确保默认的Vue应用程序正常工作:npm run serve访问http://localhost:8080/您的浏览器并检查默认Vue应用程序是否正在运行。它应该显示如下内容:接下来,您需要添加sass-loader为开发依赖项。就本教程而言,最好只安装我使用的相同版本。这是因为在撰写本文时,最新版本与Vue3不兼容:npm install sass-loader@10.1.1 --save-devnode-sass出于与上述相同的原因,您还需要安装。最好坚持使用与我相同的版本:npm install node-sass@4.14.1 --save注意:如果以这种方式安装Sass对您不起作用,您也可以在使用CLI创建Vue应用程序时选择手动选择功能。然后,选择CSSPreprocessors并选择Sass/SCSS(withdart-sass)。创建应用程序现在我们准备开始构建应用程序。在您继续操作时,请记住根目录是src文件夹。从更新main.js文件开始。这是我们导入根组件App.vue和router/index.js声明所有与路由相关的文件的地方:// main.jsimport { createApp } from "vue";import App from "./App.vue";import router from "./router";const app = createApp(App);app.use(router);app.mount("#app");设置路由器在App.vue文件中,使用router-viewVueRouter提供的组件。这是VueRouter使用的最顶层组件,它为用户访问的当前路径呈现相应的组件:// App.vue<template>  <div id="app">    <router-view />  </div></template><script>export default {  name: "App",};</script>接下来,创建一个新router/index.js文件并添加以下内容。要创建路由器,我们需要从VueRouter中提取createRouter和createWebHistory。createRouter允许我们创建一个新的路由器实例,同时createWebHistory创建一个HTML5历史记录,它基本上是HistoryAPI的包装器。当我们在页面之间导航时,它允许VueRouter操作地址栏中的地址:// router/index.jsimport { createRouter, createWebHistory } from "vue-router";在此之下,导入我们将使用的所有页面:import PokemonList from "../views/PokemonList.vue";Vue的路由器需要含有对象的数组的path,name和component作为其属性:path:这是您要匹配的模式。在下面的代码中,我们匹配根路径。因此,如果用户尝试访问http://localhost:8000,则匹配此模式。name:页面名称。这是页面的唯一标识符,当您想从其他页面导航到此页面时,将使用此标识符。component:当path与用户访问的URL匹配时要呈现的组件。const routes = [  {    path: "/",    name: "PokemonList",    component: PokemonList,  },];最后,通过提供一个包含history和routesto的对象来创建路由器实例createRouter:const router = createRouter({  history: createWebHistory(),  routes,});export default router;这就是我们现在所需要的。您可能想知道其他页面在哪里。我们稍后会添加它们。现在,让我们先处理默认页面。创建页面创建页面实际上并不需要任何特殊代码。所以如果你知道如何在Vue中创建自定义组件,你应该能够创建一个页面供VueRouter使用。创建一个views/PokemonList.vue文件并添加下面的代码。在此文件中,我们使用自定义List组件来呈现Pokemon列表。我们真正需要做的唯一一件事就是为List组件提供要使用的数据。一旦组件被挂载,我们就会向PokeAPI发出请求。我们不希望列表变得太大,因此我们将结果限制为原始的151Pokemon。一旦我们得到结果,我们只需将它分配给组件的items数据。这将依次更新List组件:<template>  <List :items="items" /></template><script>import axios from "axios";import List from "../components/List.vue";export default {  name: "PokemonList",  data() {    return {      items: null,    };  },  mounted() {    axios.get(`https://pokeapi.co/api/v2/pokemon?limit=151`).then((res) => {      if (res.data && res.data.results) {        this.items = res.data.results;      }    });  },  components: {    List,  },};</script>这是List组件的代码。组件存储在components目录中,因此创建一个components/List.vue文件并添加以下内容:<template>  <div v-if="items">    <router-link      :to="{ name: 'Pokemon', params: { name: row.name } }"      class="link"      v-for="row in items"      :key="row.name"    >      <div class="list-item">        {{ row.name }}      </div>    </router-link>  </div></template><script>export default {  name: "List",  props: {    items: {      type: Array,    },  },}; </script> <style lang="scss" scoped>@import "../styles/list.scss";</style>您可以styles/list.scss在GitHubrepo【https://github.com/sitepoint-editors/pokedex-vue-router/blob/master/src/styles/list.scss】中查看该文件的代码。此时,您现在可以在浏览器中查看更改。除非您收到以下错误:这是因为Vue正在尝试生成Pokemon页面的链接,但还没有。VueCLI足够聪明,可以警告你这一点。您可以通过使用a<div>代替components/List.vue文件模板来临时解决此问题:<template>  <div v-if="items">    <div v-for="row in items" :key="row.name">{{ row.name }}</div>  </div></template>有了这个,你应该能够看到口袋妖怪的列表。一旦我们添加了Pokemon页面,请记住稍后将其更改回来。声明式导航使用VueRouter,您可以通过两种方式导航:声明式和编程式。声明式导航与我们在HTML中使用锚标记所做的几乎相同。您只需声明您希望链接导航到的位置。另一方面,编程导航是通过在执行用户操作(例如单击按钮按钮)时显式调用VueRouter以导航到特定页面来完成的。让我们快速分解一下这是如何工作的。要导航,您需要使用该router-link组件。这需要的唯一属性是:to。这是一个包含name要导航到的页面的对象,以及一个params用于指定要传递给页面的参数的可选对象。在这种情况下,我们传入Pokemon的名称:<router-link  :to="{ name: 'Pokemon', params: { name: row.name } }"  class="link"  v-for="row in items"  :key="row.name">  <div class="list-item">    {{ row.name }}  </div></router-link>要可视化这是如何工作的,您需要知道Pokemon屏幕使用的模式。这里是什么样子:/pokemon/:name。:name表示name您传入的参数。例如,如果用户想查看皮卡丘,则URL将如下所示http://localhost:8000/pokemon/pikachu。我们很快就会更详细地回到这一点。路线参数我们已经看到了如何为我们的路由匹配特定的模式,但我们还没有了解如何传入自定义参数。我们已经通过router-link前面的例子简要地看到了它。我们将使用下一页( Pokemon)来说明路由参数在VueRouter中是如何工作的。为此,您需要做的就是在参数名称前加上冒号( :)。在下面的例子中,我们想传入Pokemon的名字,所以我们添加了:name. 这意味着如果我们想导航到这个特定的路线,我们需要为这个参数传入一个值。正如我们在router-link前面的示例中看到的,这是我们传递Pokemon名称的地方:// router/index.jsimport PokemonList from "../views/PokemonList.vue";import Pokemon from "../views/Pokemon"; // add thisconst routes = [  {    path: "/",    name: "PokemonList",    component: PokemonList,  },  // add this:  {    path: "/pokemon/:name",    name: "Pokemon",    component: Pokemon,  }]这是Pokemon页面( views/Pokemon.vue)的代码。就像之前的PokemonList页面一样,我们将渲染UI的任务委托给一个单独的组件BasicDetails。当组件被挂载时,我们向API的/pokemon端点发出请求。要获取作为路由参数传入的Pokemon名称,我们使用this.$route.params.name. 我们正在访问的属性应该与您为router/index.js文件中的参数提供的名称相同。在这种情况下,它是name. 如果您用于代替,则可以使用/pokemon/:pokemon_name以下命令path访问它this.$route.params.pokemon_name:<template>  <BasicDetails :pokemon="pokemon" /></template><script>import axios from "axios";import BasicDetails from "../components/BasicDetails.vue";export default {  name: "Pokemon",  data() {    return {      pokemon: null,    };  },  mounted() {    const pokemon_name = this.$route.params.name;    axios      .get(`https://pokeapi.co/api/v2/pokemon/${pokemon_name}`)      .then((res) => {        const data = res.data;        axios          .get(`https://pokeapi.co/api/v2/pokemon-species/${pokemon_name}`)          .then((res) => {            Object.assign(data, {              description: res.data.flavor_text_entries[0].flavor_text,              specie_id: res.data.evolution_chain.url.split("/")[6],            });            this.pokemon = data;          });      });  },  components: {    BasicDetails,  },};</script>这是BasicDetails组件( components/BasicDetails.vue)的代码:<template>  <div v-if="pokemon">    <img :src="pokemon.sprites.front_default" :alt="pokemon.name" />    <h1>{{ pokemon.name }}</h1>    <div class="types">      <div        class="type-box"        v-for="row in pokemon.types"        :key="row.slot"        v-bind:class="row.type.name.toLowerCase()"      >        {{ row.type.name }}      </div>    </div>    <div class="description">    {{ pokemon.description }}    </div>    <a @click="moreDetails" class="link">More Details</a>  </div></template><script>export default {  name: "BasicDetails",  props: {    pokemon: {      type: Object,    },  },  methods: {    moreDetails() {      this.$router.push({        name: "PokemonDetails",        params: {          name: this.pokemon.name,          specie_id: this.pokemon.specie_id,        },      });    },  },};</script>  <style lang="scss" scoped>@import "../styles/types.scss";@import "../styles/pokemon.scss";</style>您可以在GitHub存储库【https://github.com/sitepoint-editors/pokedex-vue-router/blob/master/src/styles】中查看styles/types.scss和styles/pokemon.scss文件的代码。此时,您应该能够再次在浏览器中看到更改。您还可以将components/List.vue文件更新回其原始代码,router-link而不是<div>.程序化导航您可能已经注意到我们在BasicDetails组件中做了一些不同的事情。我们并没有真正使用导航到PokemonDetails页面router-link。相反,我们使用了一个锚元素并拦截了它的点击事件。这就是程序化导航的实现方式。我们可以通过访问路由器this.$router。然后我们调用push()方法在历史堆栈的顶部推送一个新页面。路由器将显示顶部的任何页面。当用户单击浏览器的后退按钮时,此方法允许导航回上一页,因为单击它只是将当前页面“弹出”到历史堆栈的顶部。此方法接受一个包含name和params属性的对象,因此它与您传递给to财产在router-link:methods: {    moreDetails() {      this.$router.push({        name: "PokemonDetails",        params: {          name: this.pokemon.name,          specie_id: this.pokemon.specie_id,        },      });    },  },嵌套路由接下来,更新路由器文件以包含Pokemon详细信息页面的路径。在这里,我们使用嵌套路由传入多个自定义参数。在这种情况下,我们传入nameand specie_id:import Pokemon from "../views/Pokemon";import PokemonDetails from "../views/PokemonDetails"; // add thisconst routes = [  // ..  {    path: "/pokemon/:name",    // ..  },  // add these  {    path: "/pokemon/:name/:specie_id/details",    name: "PokemonDetails",    component: PokemonDetails,  },];这是PokemonDetails页面( views/PokemonDetails.vue)的代码:<template>  <MoreDetails :pokemon="pokemon" /></template><script>import axios from "axios";import MoreDetails from "../components/MoreDetails.vue";export default {  name: "PokemonDetails",  data() {    return {      pokemon: null,    };  },  mounted() {    const pokemon_name = this.$route.params.name;    axios      .get(`https://pokeapi.co/api/v2/pokemon/${pokemon_name}`)      .then((res) => {        const data = res.data;        axios.get(`https://pokeapi.co/api/v2/evolution-chain/${this.$route.params.specie_id}`)        .then((res) => {          let evolution_chain = [res.data.chain.species.name];          if (res.data.chain.evolves_to.length > 0) {            evolution_chain.push(              res.data.chain.evolves_to[0].species.name            );            if (res.data.chain.evolves_to.length > 1) {              const evolutions = res.data.chain.evolves_to.map((item) => {                return item.species.name;              }            );            evolution_chain[1] = evolutions.join(" | ");          }          if (            res.data.chain.evolves_to[0].evolves_to.length >            0          ) {            evolution_chain.push(res.data.chain.evolves_to[0].evolves_to[0].species.name);          }            Object.assign(data, {              evolution_chain,            });          }          this.pokemon = data;        });    });  },  components: {    MoreDetails,  },};</script>这是MoreDetails组件( components/MoreDetails.vue)的代码:<template>  <div v-if="pokemon">    <h1>{{ pokemon.name }}</h1>    <div v-if="pokemon.evolution_chain" class="section">      <h2>Evolution Chain</h2>      <span v-for="(name, index) in pokemon.evolution_chain" :key="name">        <span v-if="index">-></span>        {{ name }}      </span>    </div>    <div v-if="pokemon.abilities" class="section">      <h2>Abilities</h2>      <div v-for="row in pokemon.abilities" :key="row.ability.name">        {{ row.ability.name }}      </div>    </div>    <div v-if="pokemon.moves" class="section">      <h2>Moves</h2>      <div v-for="row in pokemon.moves" :key="row.move.name">        {{ row.move.name }}      </div>    </div>  </div></template><script>export default {  name: "MoreDetails",  props: {    pokemon: {      type: Object,    },  },};</script><style lang="scss" scoped>@import "../styles/more-details.scss";</style>此时,您可以单击任何Pokemon名称并查看单个Pokemon的详细信息。您可能需要重新启动服务器才能看到更改。404页面我们已经为所有页面添加了代码。但是如果用户在浏览器的地址栏中输入无效的URL会发生什么?在这些情况下,它只会出错或根本不显示任何内容。我们需要添加一种拦截这些请求的方法,以便我们可以显示“404notfound”页面。为此,请打开路由器文件并导入NotFound页面:import NotFound from "../views/NotFound";路由根据它们在路由数组中添加的顺序进行优先级排序。这意味着首先添加的那些是第一个与用户在地址栏上输入的URL匹配的。所以404页面的模式必须最后添加。在routes数组中,添加以下内容:const routes = [const routes = [  // ..  {    path: "/pokemon/:name/:specie_id/details",    // ..  },  // add this  {    path: "/:pathMatch(.*)*",    name: "NotFound",    component: NotFound,  },];path看着是不是很眼熟?我们使用一个自定义参数pathMatch来匹配输入的任何URL。因此,如果用户输入http://localhost:8000/hey或http://localhost:8000/hey/jude,它将呈现NotFound页面。这一切都很好。但是,如果包罗万有模式之上的模式实际上是匹配的,会发生什么呢?例如:http://localhost:8000/pokemon/someinvalidpokemonhttp://localhost:8000/pokemon/someinvalidpokemon/99999/details在这些情况下,包罗万象的模式将不匹配,因此我们需要一种拦截此类请求的方法。这类请求的主要问题是用户假设某个Pokemon或物种ID存在,但事实并非如此。检查的唯一方法是拥有有效Pokemon的列表。在您的路由文件中,导入有效Pokemon的列表:import NotFound from "../views/NotFound";import valid_pokemon from "../data/valid-pokemon.json"; // add this您可以在GitHub【https://github.com/sitepoint-editors/pokedex-vue-router/blob/master/src/data/valid-pokemon.json】存储库上找到此文件。为了拦截这些类型的请求,VueRouter提供了导航守卫。将它们视为导航过程的“挂钩”,允许您在VueRouter导航到某个页面之前或之后执行某些操作。我们将只经历导航完成之前执行的那个,因为这允许我们在导航到该页面的条件不匹配时重定向到另一个页面。为了在导航完成之前挂钩当前请求,我们调用实例beforeEach()上的方法router:const router = createRouter({    // ..  });   router.beforeEach(async (to) => {    // next: add the condition for navigating to the 404 page  });VueRouter传递两个参数给它:to:目标路线位置from:当前路线位置每一个都包含这些属性。我们感兴趣的是params,因为它包含用户在URL中传递的任何参数。这是我们的情况。我们首先检查我们要检查的参数是否存在。如果是,我们继续检查它是否有效。Pokemon页面的第一个条件匹配。我们使用之前的valid_pokemon数组。我们将它与to.params.name包含用户传递的Pokemon名称的进行比较。另一方面,PokemonDetails页面的第二个条件匹配。在这里,我们正在检查物种ID。因为我们只想匹配原始的101Pokemon,任何大于这个的ID都被认为是无效的。如果它符合这些条件中的任何一个,我们只需返回404页面的路径。如果条件不匹配,它将导航到它最初打算导航到的位置:if (  to.params &&  to.params.name &&  valid_pokemon.indexOf(to.params.name) === -1) {  return "/404";}if (  (to.params &&    to.params.name &&    to.params.specie_id &&    valid_pokemon.indexOf(to.params.name) === -1 &&    to.params.specie_id < 0) ||  to.params.specie_id > 101) {  return "/404";}这是404页面( views/NotFound.vue)的代码:<template>    <h1>404 Not Found</h1>  </template>  <script>  export default {    name: "Not Found",  };  </script>  <style lang="scss" scoped>  @import "../styles/notfound.scss";  </style>您可以styles/notfound.scss在GitHub【https://github.com/sitepoint-editors/pokedex-vue-router/tree/master/src/styles/notfound.scss】存储库上查看该文件的代码。至此,应用程序完成!您可以尝试访问无效页面,它会返回404页面。结论而已!在本教程中,您学习了使用VueRouter的基础知识。设置路由器、传递自定义参数、在页面之间导航以及实现404页面等事情将为您带来很长的路要走。如果您想了解从这里开始的方向,我建议您探索以下主题:将道具传递给路由组件:允许您将视图组件与路由参数分离。这提供了一种将路由参数与可以从组件访问的道具交换的方法。这样你就可以在任何没有$route.params.Transitions:用于动画页面之间的过渡。延迟加载:这更多是一种性能改进,因此打包器不会为单个文件中的所有页面编译代码。相反,它会延迟加载,以便浏览器仅在需要时才下载特定页面的代码。...

看到“游戏开发大神毛星云离世”的消息后,作为Web前端开发或后台程序员的我们需要思考些什么?

Web前端开发程序员【昂不是大神】,是深有体会。在IT行业外的很多朋友,经常会给IT技术的人贴上一些标签:IT男、宅、地中海(秃顶),找不到女朋友等等。的确,作为开发人员,压力非常大,整天对着电脑,看着一堆的代码,超负荷的脑力劳动。至于加班,那是常有的事情,有时候还会通宵,再好的身体也会被拖垮。面对压力,程序员应该需要思考什么?面对长时间、超负荷的脑力劳动,程序员们需要大量耗费自己的身体机能;一旦你没有调整好,就会出现问题,造成严重的后果。所以我们需要采取一些措施去缓解压力。接下来分享下我的一些经验。上面是我的在KEEPAPP上的数据,我觉得运动能够大大减少你身体的负荷,很大程度缓解你的精神压力,并且运动完后,你的工作效率会提高很多。大家可以参考一下我的减压方法:1、每周跑步2-3次,如果你肺活量不够,可以先尝试快走+慢跑,慢慢就会习惯。2、有时候加班太晚,回到家可以做做简单的锻炼,不但可以释放一些压力,还可以达到健身的效果,我每天下班很累,回到住处,我一般会利用烧水洗澡这段时间做一些俯卧撑、蹲起和仰卧起坐,直到满头大汗,然后美美的洗个热水澡,那时候感觉太舒服了,什么压力都忘光了。3、平时多和朋友聊聊天,这样有利于缓解自己的工作压力,如果你有一两个知心朋友,那你们可以互相抱怨一下工作的压力,不要挤满在自己的内心,我们应该把这些压力释放掉一些,剩下的压力把它转化为动力,我每次和我的好朋友倾述完我都觉得轻松了不少,我觉得这个释放压力的方法还是可以的,但我不建议你经常抱怨生活,抱怨工作,你偶尔抱怨一下还是可以的。4、骑行-我住在深圳,交通压力也大,所以绿色出行非常不错,平时约几个朋友,一路骑行,观赏美丽风景,偶尔会度假。回来后,发现心情好太多了。总结作为程序员,需要比别人花费更多的时间很精力去应付我们的工作。超负荷的工作压力随时都会压垮我们的身体,所以锻炼是最好的方法。俗话说:身体是革命的本钱,身体没了,什么也没了。最后在此也提醒大家多锻炼,保护好自己的身体,钱是赚不完的。...

如何使用CSS3混合模式在图像上显示酷炫“Web前端之家”文字

Web前端之家”文字为例子咯:看到图片后,有没有感到惊叹,文字还可以这样做特效?没错,一切只需要用css图片属性去完成。介绍图像背景在文本后面看起来很棒。然而,它的CSS实现并不是那么简单。我们可以使用该background-clip:text;属性,但它仍然是一个没有足够浏览器支持的实验性功能。在文本后面显示图像背景的CSS替代方法是使用mix-blend-mode属性。所有现代桌面和移动浏览器都支持HTML元素的混合模式,但少数浏览器除外,例如InternetExplorer。在这篇文章中,我们将看到mix-blend-mode(特别是它的两种模式)是如何工作的,以及如何在两个用例中使用它来显示带有图像背景的文本:当可以通过文本看到背景图像时当背景图像围绕文本运行时接下来我们就开始以上述截图为实例,解析如何实现。效果解析该mix-blend-modeCSS属性定义如何在内容和背景中的HTML元素应该colorwise糅合在一起。看一看在混合模式的名单,其中有我们将使用multiply和screen在这个岗位。首先,让我们看看这两种特定的混合模式是如何工作的。如何multiply与screen混合模式工作从技术上讲,混合模式是使用元素及其背景的颜色分量计算最终颜色值的函数。该multiply混合模式在multiply混合模式下,元素及其背景的各个颜色相乘,产生的颜色应用于最终的混合版本。所述multiply混合模式根据所计算的以下公式:B(Cb,Cs)=Cb×Cs其中: Cb–背景的 Cs颜色成分B–源元素的颜色成分 –混合函数当Cb和Cs相乘时,产生的颜色是这两种颜色成分的混合,并且与两种颜色中最暗的颜色一样深。要创建我们的文本图像背景,我们需要关注Cs(源元素的颜色分量)是black或white的情况。如果Cs是黑色,其值为0,则输出颜色也将为黑色,因为Cb×0=0。因此,当元素着色为黑色时,背景是什么颜色并不重要,混合后我们只能看到黑色。如果Cs是白色,则其值为1,则输出颜色为任意颜色Cb,因为Cb×1=Cb。所以在这种情况下,我们直接看到的大背景下,因为它是。该screen混合模式该screen混合模式的工作方式类似于multiply混合模式,但有相反的结果。因此,黑色前景显示背景原样,白色前景显示白色背景。让我们快速看看它的公式:B(Cb,Cs)=Cb+Cs-(Cb×Cs)当Cs为黑色(0)时,混合后将显示背景颜色,如下所示:Cb+0-(Cb×0)=Cb+0-0=Cb当Cs是白色(1)时,任何背景下的结果都是白色的,如:Cb+1-(Cb×1)=Cb+1-Cb=1通过文字显示的图像通过其背景图像表示显示文本中,我们使用screen混合模式与黑色文本和白色周围空间。例子走起。。。<div class="backdrop"> <p class="text">Web前端之家</p></div>.backdrop {            width: 800px;             height: 210px;            background-image: url(https://images.unsplash.com/reserve/AXx3QORhRDKAMrbb8pX4_photo%202.JPG);            background-size: 100%;            margin: auto;            white-space: nowrap;}.text {            color: black;            background-color: white;            mix-blend-mode: screen;            width: 100%;            height: 100%;            font-size: 80pt;            font-weight: bolder;            text-align: center;            line-height: 210px;            margin: 0;}目前我们的文本如下所示,在下一步中,我们将为背景添加自定义颜色。添加颜色正如您现在可能已经猜到的那样,使用混合模式让我们在文本周围的区域只有两种颜色选择——黑色或白色。但是对于白色,如果覆盖颜色与使用的图像很好地匹配,则可以使用覆盖为其添加一些颜色。要为周围区域添加颜色,请<div>在HTML中为叠加层添加,并为其指定具有高透明度的背景色。还可以使用mix-blend-mode:multiply叠加层的属性,因为它有助于叠加层的背景颜色与出现在文本中的图像更好地融合。<div class="backdrop">        <p class="text">Web前端之家</p>        <div class="overlay"></div></div>.overlay {            background-color: rgba(0,255,255,.1);            mix-blend-mode: multiply;            width: 100%;            height: 100%;            position: absolute;            top: 0;}效果有变化了。如下图:请注意,该技术仅适用于微妙的透明颜色。如果您使用完全不透明的颜色,或与图像不匹配的颜色,则出现在文本内的图像将具有所用颜色的非常明显的色调,因此除非您想要这种外观,否则请避免使用不透明的颜色。文字被图片背景包围尽管在图像背景上放置普通文本需要相同的技术,但我将向您展示当效果反转时上述演示的外观示例,即当文本颜色为白色而背景为黑色时。.text {            color: white;            background-color: black;            mix-blend-mode: screen;            width: 100%;            height: 100%;            font-size: 160pt;            font-weight: bolder;            text-align: center;            line-height: 210px;            margin: 0;}您可以使用相同的叠加层为文本添加一些颜色,除非您想将其保持为白色。.overlay {            background-color: rgba(0,255,255,.1);            mix-blend-mode: multiply;            width: 100%;            height: 100%;            position: absolute;            top: 0;}效果如下:请注意,在InternetExplorer或任何其他不支持该mix-blend-mode属性的浏览器中,图像背景不会出现,文本将保持黑色(或白色)。终于最上面截图的效果,我们可以换成背景图既可以实现了,谈到这里可能有些童鞋对于原理还是不懂,那么把DEMO弄出来对照就很清晰了。<!DOCTYPE html><html><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width">  <title>如何使用CSS3混合模式在图像上显示酷炫“Web前端之家”文字 | Web前端之家www.jiangweishan.com</title>  <style>    .backdrop {      width: 1000px;       height: 300px;      position: relative;      background-size: 100%;      margin: 40px auto;    }    .text {      color: black;      background-color: white;      mix-blend-mode:screen;       width: 100%; height: 100%;      font-weight: bolder;      text-align: center;      line-height: 300px;      margin: 0;    }    .inverse {      color: white;      background-color: black;    }    .backdrop    ,.text {      border-radius: 2px;    }    .overlay {      width: 100%; height: 100%;      position: absolute;      top: 0;      mix-blend-mode:multiply;    }    /* water */    .water.backdrop {      background-image: url(https://images.unsplash.com/reserve/AXx3QORhRDKAMrbb8pX4_photo%202.JPG);    }    .water.overlay {      background-color: rgba(0,255,255,.1);    }    .water.text {      font-size: 60pt;    }    /* fire */    .fire.backdrop {      background-image: url(https://66.media.tumblr.com/e454398961e0203f2aad001cf7d177bf/tumblr_mrztj7GFJA1sdyj9lo1_1280.jpg);    }    .fire.overlay{      background-color: rgba(255,0,0,.1);    }    .fire.text{      font-size: 100pt;    }  </style></head><body>  <div class="backdrop fire">    <p class="text fire">Web前端之家</p>    <div class="overlay fire"></div>  </div>    <div class="backdrop fire">    <p class="text fire inverse">Web前端之家</p>    <div class="overlay fire inverse"></div>  </div>    <div class="backdrop water">    <p class="text water">Web前端之家</p>    <div class="overlay water"></div>  </div>    <div class="backdrop water">    <p class="text water inverse">Web前端之家</p>    <div class="overlay water"></div>  </div></body></html>运行试试吧。总结如果你觉得不错的话,留言吧,如果想学习更多的效果和技术,请加QQ群一起探讨。...

Web前端表单开发:HTML5表单和Constraint Validation完整指南

Web前端表单开发:HTML5表单和Constraint Validation完整指南 | Web前端之家www.jiangweishan.com</title>  <style>    * {  font-family: sans-serif;  font-size: 1em;}div {  width: 100%;  max-width: 15em;  margin: 1em auto;}label, input {  display: block;  width: 100%;}input {  padding: 0.5em;  border: 1px solid #666;  border-radius: 4px;}button {  display: block;  margin: 0 auto;}.help {  display: none;  font-size: 0.8em;  text-align: center;  margin: 0.2em 0 1em 0;  color: #c00;}.invalid .help {  display: block;}.invalid label, .invalid input {  color: #c00;  border-color: #c00;}  </style></head><body>    <form id="contact" action="./" method="post">        <div>          <label for="username">注册Web前端之家账号</label>          <input type="text" autocomplete="username" id="username" name="username" required />          <p class="help">Please enter your username.</p>        </div>              <div>          <label for="email">注册Web前端之家邮箱</label>          <input type="email" autocomplete="email" id="email" name="email" />          <p class="help">Please enter your email or telephone.</p>        </div>              <div>          <label for="tel">手机号码</label>          <input type="tel" autocomplete="tel" id="tel" name="tel" />          <p class="help">Please enter your email or telephone.</p>        </div>              <div>          <button type="submit">submit</button>        </div>              </form>      <script>          // custom form validation            class FormValidate {                        constructor(form, field) {                                // active form                this.form = form;                this.form.noValidate = true;                // custom validation functions                this.custom = [];                // validate fields on focus change?                this.validate = !!field;                                // field focusout event                this.form.addEventListener('focusout', e => this.changeHandler(e) );                // form submit event                this.form.addEventListener('submit', e => this.submitHandler(e) );                            }                                    // add a custom validation function            // it's passed the field and must return true (valid) or false (invalid)            addCustom(field, vfunc) {                // get index                let c = field.CustomValidator;                if (typeof c === 'undefined') {                c = this.custom.length;                field.CustomValidator = c;                }                // store function reference                this.custom[c] = (this.custom[c] || []).concat(vfunc);            }                                    // validate a field when focus changes            changeHandler(e) {                const t = e.target;                if (this.validate && t && t.checkValidity) this.validateField(t);            }                                    // validate all fields on submit            submitHandler(e) {                // validate all fields                let first, invCount = 0;                Array.from(this.form.elements).forEach(f => {                if (!this.validateField(f)) {                    // find first visible invalid                    if (f.offsetHeight) first = first || (f.focus && f);                    invCount++;                }                });                // at least one field is invalid                if (invCount) {                // stop submission                e.stopImmediatePropagation();                e.preventDefault();                // enable field focusout validation                this.validate = true;                // focus first invalid field                if (first) {                    first.parentElement.scrollIntoView(true);                    setTimeout(() => first.focus(), 800);                }                }                // form is valid - submit                else if (this.submit) this.submit(e);            }                                    // validate a field            validateField(field) {                const                parent = field.parentElement,                c = field.CustomValidator,                inv = 'invalid';                field.setCustomValidity('');                // default validation                let valid = field.checkValidity();                // custom validation                if (valid && typeof c !== 'undefined') {                valid = !this.custom[c].some(fn => !fn(field));                }                if (valid) {                // field is valid                parent.classList.remove(inv);                return true;                }                else {                // field is not valid                field.setCustomValidity(inv);                parent.classList.add(inv);                return false;                }            }                        }            // ___________________________________________________________________            // validate contact form            const contactForm = new FormValidate(document.getElementById('contact'), false);            // custom validation - email and/or telephone            const            email = document.getElementById('email'),            tel = document.getElementById('tel');            contactForm.addCustom(email, f => f.value || tel.value);            contactForm.addCustom(tel, f => f.value || email.value);            // custom submit            contactForm.submit = e => {            e.preventDefault();            alert('Form is valid!\n(open the console)');            const fd = new FormData(e.target);            for (const [name, value] of fd.entries()) {                console.log(name + ': ' + value);            }            }      </script></body></html>它是使用名为的通用表单验证类实现的FormValidate。实例化对象时传递表单元素。可以设置可选的第二个参数:true 在用户与其交互时验证每个字段false (默认)在第一次提交后验证所有字段(在此之后进行字段级验证)// validate contact formconst contactForm = new FormValidate(document.getElementById('contact'), false);一个.addCustom(field,func)方法定义了自定义验证函数。以下代码确保email或tel字段有效(都没有required属性):// custom validation - email and/or telephoneconst  email = document.getElementById('email'),  tel = document.getElementById('tel');  contactForm.addCustom(email, f => f.value || tel.value);  contactForm.addCustom(tel, f => f.value || email.value);一个FormValidate对象监视以下两个:focusout 事件,然后检查单个字段表单submit事件,然后检查每个字段两者都调用该.validateField(field)方法,该方法检查字段是否通过标准约束验证【ConstraintValidation】。当它这样做时,分配给该字段的任何自定义验证功能将依次执行。必须全部返回true才能使该字段有效。无效字段具有invalid应用于该字段的父元素的类,该类使用CSS显示红色帮助消息。最后,submit当整个表单有效时,对象调用自定义函数:// custom submitcontactForm.submit = e => {  e.preventDefault();  alert('Form is valid!\n(open the console)');  const fd = new FormData(e.target);  for (const [name, value] of fd.entries()) {    console.log(name + ': ' + value);  }}或者,您可以使用标准addEventListener来处理表单submit事件,因为FormValidate当表单无效时可以防止进一步的处理程序运行。形式技巧表单是所有Web应用程序的基础,开发人员花费大量时间处理用户输入。约束验证【ConstraintValidation】得到很好的支持:浏览器可以处理大多数检查并显示适当的输入选项。建议:尽可能使用标准的HTML输入类型。集min,max,step,minlength,maxlength,pattern,required,inputmode,和autocomplete属性适当。如有必要,使用一点JavaScript来启用自定义验证和消息。对于更复杂的字段,逐步增强标准输入。最后:忘记InternetExplorer!除非您的客户主要是IE用户,否则没有必要实现您自己的回退验证功能。所有HTML5输入字段都可以在IE中使用,但可能需要更多的用户努力。(例如,当您输入无效的电子邮件地址时,IE不会检测到。)您仍然需要验证服务器上的数据,因此请考虑将其用作IE错误检查的基础。...

Web前端之家带您全方位了解网页开发优化工具:Lighthouse

您已经建立了一个网站,但它对您的最终用户的表现如何?问这个问题很重要,但你怎么能得到答案呢?一个很好的选择是使用Lighthouse。Lighthouse允许您轻松自动地分析网页的性能和质量。它提供的工具可让您了解如何提高网页的性能、可访问性、搜索引擎优化等。使用这些分数来增强您的网站最终将吸引更多用户并提高您网站在搜索引擎中的排名。在本文中,我们将探讨网站性能的重要性,这是Lighthouse评分的亮点。我们还将看看为什么我们应该使用Lighthouse;如何通过Chrome和LighthouseCLI使用Lighthouse;以及Lighthouse可以为您的网页进行评分的各种审计,以及它们为何重要。我们还将介绍如何解决一些常见的性能和其他网站问题。我们还将了解如何使用WebPageTest,这是另一个用于评估站点性能的强大工具。为什么性能很重要如果您查看一些世界上最好的网站,就会发现它们都有一个共同点:它们为用户提供卓越的体验。造成这种情况的原因有很多,但最重要的原因之一是性能。用户喜欢快速的网站。他们等待网站加载的时间越长,他们就越有可能离开网站寻找更好的网站,让他们无需等待就可以做他们需要做的事情。提高网站的性能可以提高SEO排名(我们将在本文后面的更多细节中看到),并提高点击率和转化率。因此,如果您想要更多的销售、注册或流量,您首先必须确保您网站的性能达到标准。为什么使用Lighthouse Lighthouse是Google创建的开源工具。由于Google是一家受到许多人信任的信誉良好的公司,因此它使结果比其他工具更可靠。此外,由于Google的搜索引擎是过去十年中领先的搜索引擎,因此在其搜索引擎上排名靠前很可能会带来更多流量。因此,在他们的性能审计工具上获得更高的分数可能会提高您网站在搜索结果中的排名。但除了Lighthouse是由Google创建的这一事实之外,使它成为一个好工具的原因在于它的详细结果将用户的最大利益放在心上。Lighthouse提供了许多结果,涉及页面加载所需的时间、页面(以及最终网站)是否安全、页面的可访问性实践和实施是否允许所有用户的包容性等等。如何使用Lighthouse 在本节中,我们将首先介绍使用Lighthouse分析和检查网页分数的最简单方法,该方法直接通过Chrome完成。然后,我们将检查可能提供更详细结果或选项的其他Lighthouse工具。使用Chrome通过Chrome使用Lighthouse是衡量网站性能的最简单方法。首先,打开您想要测量的任何网页。为了演示,我将使用在我的网站和GitHub上运行的审计的一些结果,以便我们查看各种示例结果。接下来,通过单击shift+ control+J或F12在Windows/Linux上或在macOS上单击option+ command+J或fn+F12打开开发工具。之后,单击Lighthouse 选项卡。如您所见,您可以选择希望审核包括哪些类别。这些类别是性能、渐进式Web应用程序、最佳实践、可访问性和SEO。您还可以选择是否希望在移动或桌面上运行审核。在本节中,我们将执行四项审核,即性能、最佳实践、可访问性和SEO,我们将在桌面上运行它们。但是,为了获得最佳结果,重要的是在不同条件下测试您的网页-例如在移动设备和不同的互联网速度下。因此,在您要衡量的网页上,继续选择提到的四个类别和桌面,然后单击生成报告。这可能需要几分钟的时间,您会看到浏览器的窗口在此过程中可能会增大和缩小。这是因为报告需要不同的屏幕尺寸和网页条件。完成后,Lighthouse选项卡将包含审核结果,其中包括性能、最佳实践、可访问性和SEO的得分。应该注意的是,有时结果可能会因您的互联网连接而有所不同。此外,有时Lighthouse会显示一些扩展可能影响结果的通知。如果是这样,最好在隐身窗口中运行测试。Lighthouse完成运行后,您可以单击任何类别以查看更详细的报告。表现性能侧重于如何使您的网站加载速度更快。我们将讨论性能的六个重要指标,它们各自的含义以及如何在每个指标中获得更高的分数。首次内容绘制(FCP)第一次内容绘制(FCP)是从用户导航到网页的那一刻开始测量的。它测量加载第一个DOM内容需要多长时间。DOM内容可以包括图像、非白色画布元素和SVG元素。FCP以秒为单位。为了让您的网页获得“绿色”分数——这意味着快速的FCP——加载第一段DOM内容最多需要1.8秒。一旦超过该值,它就会变得中等或缓慢。FCP很重要的原因可以追溯到了解用户。如前所述,用户不耐烦,如果加载时间过长,他们就会离开您的网站。快速的FCP可以让用户保持参与。它可以像加载屏幕一样简单,它告诉用户要紧紧抓住,因为网页即将加载。可能影响您网站FCP时间的因素之一是字体。字体可能需要很长时间才能加载,有时会在加载之前使文本不可见。解决这个问题的一个好方法是在使用font-display:swap;.css声明字体时使用CSS属性@font-face。例如:@font-face{font-family:'MyFont';font-style:normal;font-weight:400;src:url(MyFont.woff);font-display:swap;}如果您使用的是Google字体,现在默认启用此属性。此属性允许使用用户的系统默认字体显示文本,直到加载网站的字体。影响FCP的另一个因素是渲染阻塞资源。这些通常是同步加载的JavaScript或CSS文件,<head>需要很长时间才能加载。这些资源将迫使页面的其余部分在页面完全加载之前等待它们被加载,从而影响DOM内容的FCP。以下是您可以解决此问题的一些方法:将defer或async属性添加到您的<script>标签中。例如:<scriptsrc="myScript.js"defer></script>缩小页面上使用的JavaScript和CSS文件,减小其大小,以便加载这些资源不会花费很长时间。这取决于您使用的是哪种框架或编程语言,但几乎所有这些框架或编程语言都有这样做的方法。您可以在web.dev站点上查看有关在使用各种流行框架时如何缩小JavaScript的简单提示。删除网页中未使用的CSS。随着CSS数量的增加,通过删除页面上未使用的不必要规则和属性来减少CSS文件可以使其加载速度更快。同样,根据您使用的框架,有不同的方法可以做到这一点。但是,您可以使用Chrome的开发人员工具在Coverage选项卡中查看未使用的CSS 。使用像TailwindCSS【http://tailwindcss.com/】这样的框架——它使用摇树机制来减少你的CSS样式,而不是用你可能永远不会使用的许多样式来膨胀你的网站——可以帮助加快你的网站加载速度。减少服务器的响应时间。这可以通过执行使用异步请求加载页面所需的逻辑、缓存服务器经常使用的数据或优化服务器代码以更快地获得所需结果来完成。当您执行性能审核时,如果您的网页存在字体问题、内容阻塞资源、未使用的CSS或上述其他问题,Lighthouse将突出显示导致问题的每个文件并提供有关如何解决的提示得到改进。例如,如果字体阻止了文本的可见性,Lighthouse将向您显示导致问题的字体文件以及解决该问题将如何使页面的FCP时间更快。这是GitHub审计报告中的一个示例,该示例将CSS文件显示为“阻止渲染的资源”。当您阅读其余的指标时,您会意识到它们在某种程度上取决于这个指标的结果。因此,在FCP中取得好成绩很重要。交互时间(TTI)交互时间(TTI)衡量您的用户能够与网页完全交互所需的时间。TTI的结果取决于FCP,因为FCP越快,用户与网页交互的速度就越快。用户不仅需要看到加载的页面,还需要能够与之交互。如果网站看起来已满载,但用户无法与其正确交互,则甚至会变得更加烦人。例如,单击按钮不会执行任何操作,即使它在屏幕上看起来可以使用。改善您网站的TTI将使您的用户更容易使用您的网站,从而提高转化率。为了让一个网页被认为是完全活跃的,它首先需要实现FCP。然后,应该为页面上可见的元素注册事件处理程序。最后,页面应该能够在50毫秒内响应用户的交互。网页的TTI以秒为单位。要使网页具有良好的TTI,最多需要3.8秒才能完全交互。如果页面花费的时间比这更长,它将被视为中等或缓慢。有几个因素会影响网页的TTI分数。最常见的是“主线程工作”和“JavaScript执行时间”。主线程工作意味着浏览器呈现页面所需的过程。对于浏览器呈现页面,它首先解析HTML,然后构建DOM,解析CSS,并执行JavaScript。如果这个过程花费的时间太长,这意味着页面也需要很长时间才能准备好响应用户的交互,因为处理事件也是主线程工作的一部分。JavaScript执行时间包括获取脚本、解析它、编译它、执行它所花费的时间以及它可能使用的内存。如果由于脚本的大小,获取脚本需要很长时间,则需要更长的时间才能解析和执行它。脚本获取后,解析编译发生在主线程工作中,所以如果时间长,会拖慢主线程工作。如果脚本在页面加载时有大量代码要执行,这发生在主线程工作上,则网页需要很长时间才能为用户交互做好准备。改进TTI指标与FCP非常相似,因为它首先依赖于它。好的做法是缩小JavaScript和CSS文件并删除其中所有未使用的代码。您可以做的另一件事是将JavaScript和CSS文件拆分为包(通常使用打包器完成,例如webpack)。这意味着网页可以更快地获取包,如果您还使用该defer属性,如上一节所述,它将以更快的速度异步获取包。总阻塞时间(TBT)这在概念上与TTI非常相似。总阻塞时间(TBT)衡量FCP和TTI之间的总时间,在此期间各种任务会阻塞用户交互。如果单个任务花费的时间超过50毫秒,那么它被认为是阻塞的,并且该点之后的时间被添加到总阻塞时间中。例如,如果某个特定任务需要90毫秒,则将40毫秒(即90毫秒–50毫秒)添加到TBT总体测量中。该指标主要强调TTI衡量的内容,并特别关注哪些任务会影响页面加载时间,以及它们对页面加载时间的影响程度。TBT以毫秒为单位。要获得良好的TBT分数,您网页上任务的总阻塞时间不应超过200毫秒。为了提高您的TBT分数,它类似于TTI。您需要尽量减少主线程工作并减少JavaScript执行。然而,随着TBT更深入地研究代码执行的细节,其他增强功能可能是优化您的JavaScript代码,使其运行得更快。例如,当从文档中选择一个元素时,请在选择器中具体化,而不是使用像div. 更深入地查看您的代码,并寻找任何改进代码的机会,这将反过来优化每项任务所需的时间。最大的内容绘制(LCP)最大内容绘制(LCP)测量加载可见视口中最大元素所需的时间。考虑的元素是<img>、<image>、<video>、具有背景图像的元素,或具有文本和内联节点作为其子元素的块元素。如果元素在当前视口中是最大的,则该元素被认为是最大的元素。这不包括未在当前视口中显示的元素的任何部分(例如,如果图像延伸到可见视口之外,则只有可见部分会被计算在内),或使用CSS添加的边距、填充或边框。由于视口中最大的元素会随着页面加载而变化,因此将针对网站中的每个新元素条目测量LCP,直到用户能够与页面进行交互。因此,如果在页面加载时,最大的元素是其中包含文本的节点,但随后加载的图像大于文本节点,则将改为测量图像的LCP。一旦用户可以与页面交互——如前所述,这是用TTI测量的——LCP将停止测量任何新条目。LCP以秒为单位。一个好的LCP是最大的内容最多需要2.5秒才能加载。与FCP类似,LCP受渲染阻塞资源的影响。它还受加载资源(例如,图像、CSS文件等)所需时间的影响。影响LCP分数的另一个因素是服务器响应所需的时间。为了改进LCP,它类似于以前讨论的方法。执行诸如延迟渲染阻塞资源、最小化CSS/JavaScript文件以及使用该font-display:swap属性等操作可以优化您的LCP分数。此外,您可以执行以下操作来提高网页的LCP分数:将CDN用于图像和视频等资产。CDN可以更快地为资产提供服务,因此可以改善这些资产的“服务器响应时间”。将图像压缩到较小的尺寸。这可以通过许多在线工具来完成,比如CompressPNG【https://compresspng.com/】。使图像变小可以减少加载所需的时间。将您的图像转换为WebP,因为它们比PNG和JPEG图像小。这也可以使用在线工具来完成。根据设备的大小正确调整图像大小。要完成最后一项任务,您可以使用<picture>元素根据设备的宽度包含图像。例如:<picture><sourcesrcset="img-small.png"media="(max-width:567px)"/><sourcesrcset="img-medium.png"media="(max-width:992px)"><imgsrc="img.png"/></picture>请注意,为了指定媒体大小,我们使用<source>标签并指定media属性。但是,为了确保我们的网页支持所有浏览器和用户代理,我们添加了<img>标签作为后备,以防<source>浏览器不支持。由于始终调整图像大小和转换图像并不理想,因此Cloudinary是一个很好的选择,可以随时为您执行此操作。Cloudinary通过根据需要调整大小和转换图像来优化您的图像,并且它们提供了一个非常好的免费计划。自动化此过程的另一种选择是使用Gulp或webpack等工具。例如,下图显示了在我的网站上运行的报告,其中包含有关图像大小的警告。速度指数正如您可能从名称中猜到的那样,速度指数指标衡量页面内容变得可见的速度。它是通过捕获页面加载的视频,然后计算帧之间的视觉进展来完成的。该指标与其他指标类似,以秒为单位。要获得良好的速度指数分数,您的页面应在开始加载后的3.4秒内加载其内容。该指标在很大程度上依赖于前面提到的指标。如果您的网页存在上述任何问题,其速度指数将会下降——例如,当图像未压缩、样式和脚本未缩小和优化时,或者主线程上正在进行大量工作时。这些问题会延迟页面内容的可见性。要提高您的速度指数,请努力改进已经讨论过的相同问题。累积布局偏移(CLS)累积布局偏移(CLS)测量页面在完全加载之前更改布局的次数。例如,当页面加载时,用户尝试点击按钮多少次,只是为了让内容移动并导致用户点击其他地方?这可能非常烦人,有时会导致用户执行他们实际上并不打算执行的操作。每次布局更改都称为布局转换。CLS测量在一秒钟内发生的多次布局变化。所以,如果你的网页有很多布局变化,它的CLS分数就会很差。CLS分数越低越好。网页的CLS分数应为0.1或以下,才能被视为快速。以下是一些避免布局偏移的提示,从而降低和提高页面的CLS分数:设置图片或视频的height和width属性,这样即使加载时间很长,一旦加载它也不会在其周围移动内容。设置这些属性后,加载时仍会为资产保留该高度和宽度的空间,加载时不会影响其周围的内容。避免使用弹出窗口或任何重叠元素,除非它们在用户与页面交互时出现——例如,当用户单击按钮时。当需要移动元素时,使用变换动画,向用户表明布局更改即将发生,而不是让他们措手不及。性能指标对SearchConsole的影响Google的SearchConsole是了解您的网站在搜索引擎中的表现的绝佳工具。您可以看到很多关于您网站的SEO的信息,包括您网站的哪些查询排名更高,用户通过执行搜索查询找到哪些页面,以及您的网页排名有多高。如果您在Google的SearchConsole上拥有一个帐户并将您的网站添加到其中,您会注意到侧边栏中有“CoreWebVitals”。WebVitals由Google创建,旨在简化改善网站用户体验的过程并使其统一。CoreWebVitals是WebVitals的子集。它们是最重要的指标,因为它们适用于所有网站,结果将显示在所有Google工具中。在撰写本文时,CoreWebVitals由FCP、LCP和CLS组成。但是,随着改善用户体验和性能的研究正在进行中,该列表有望扩大。此页面显示了当用户通过搜索引擎找到您的网站时,性能审核中的重要指标如何衡量,如果网页的指标较低,它将显示为不良URL或需要改进的URL。核心重要指标是FCP、LCP和CLS。如果您的网站或网页在这些指标中得分较低,则您网站的CoreWebVitals报告中将显示不同级别的警告。下图显示了LCP、CLS和其他问题如何在Google的SearchConsole中显示的示例。据谷歌称,网站的页面体验报告考虑了移动可用性问题和核心重要指标,被用作URL的“排名”信号。这意味着,如果网页的核心重要指标较低,则它在结果中的排名可能会较低。出于这个原因,在这三个指标中取得好成绩很重要。无障碍Lighthouse执行的下一个审计是可访问性。可访问性确保您的用户可以通过不同的设备、硬件或工具访问您的网站。它还确保残障用户能够以与其他用户相同的方式访问您的网站。辅助功能允许不同用例的用户访问您的网站,而不会影响他们的体验。以下是确保获得良好可访问性分数的一些提示:确保您的网站包含可访问的富Internet应用程序(ARIA)属性。这包括使用属性,如aria-checked,aria-labelledby,aria-describedby等等。您可以在教程“使用WAI-ARIA提高网站的可访问性”中了解有关ARIA以及如何在网站中使用属性的更多信息。使用ARIA属性时,请确保正确使用它们。例如,aria-disabled-指示输入是否被禁用-不应与像<div>.确保元素的ID是唯一的。不要aria-hidden在<body>元素上使用。确保文档有一个title元素。可访问性审核中有许多详细信息。当您执行审核时,如果有任何问题,Lighthouse会为您突出显示这些问题、发现这些问题的元素以及可能的修复方法。最佳实践最佳实践审核确保您的网页遵循使网站适合用户使用的准则。这些确保当您的用户使用该网站时,他们不会受到任何可能导致不良体验的安全问题或错误的影响。以下是确保最佳实践得分的一些提示:确保没有错误记录在浏览器的控制台中。这意味着,如果您网站上的任何页面有错误——无论是链接未加载还是代码中的错误——您都应该修复它们,以免它们再发生。确保在HTML文档的顶部,您有<!DOCTYPEhtml>.使用HTTPS保护您的网站。即使您的网站不处理敏感数据,确保您的网站对所有用户都是安全的通常也是一种很好的做法。确保您的图像以正确的纵横比呈现。例如,不应以矩形纵横比呈现方形图像。这些只是可以提高您的最佳实践分数的一些最佳实践技巧。当您在网页上运行测试时,您会看到是否有任何问题需要解决。搜索引擎优化SEO审计衡量所有不同的做法和指南,以确保您的页面在搜索引擎中排名良好。提高此审核的分数是改善网站SEO的良好开端。以下是一些提高网页SEO分数的技巧:确保您的网页具有该<title>元素,以及网页中的良好meta描述<head>。确保您的网页的字体大小在不同的屏幕尺寸上都可以阅读。不要让它们太小!不要使用带有文本“此处”或“阅读更多”的链接,而应使用描述性文本,让用户真正了解他们要去哪里。例如,如果您的链接指向您的Instagram帐户,则文本应为“支持我的Instagram帐户”。确保您的网站包含一个robots.txt文件,因为搜索引擎会使用它来抓取您的网站。您可以robots.txt在Google的创建robots.txt文件教程中了解有关如何创建好的文件的更多信息。如果您的SEO中存在任何问题,您的审计报告将向您展示问题所在以及如何解决这些问题。应该注意的是,Lighthouse提供的结果通常不足以确保您的SEO策略都相应地起作用。但这仍然是一个很好的起点。使用命令行界面检查网页分数的另一种方法是使用Lighthouse的CLI工具。CLI工具允许您使用更多选项来使报告更准确。在安装CLI工具之前,请确保已安装Chrome。此外,您需要安装Node.js,并且必须是版本12或更高版本。要检查您的Node.js版本,请运行以下命令:node-v如果一切顺利,您现在可以使用以下命令为Lighthouse安装CLI工具:npminstall-glighthouse安装后,您可以使用以下命令运行Lighthouse的分析:lighthouse<PAGE_URL>这<PAGE_URL>是您要为其生成报告的页面的URL。运行此命令后,您会注意到Chrome将打开多次,并且还会使用不同的窗口大小。您还可以在终端中看到对正在执行的每个测试的详细分析。审计完成后,带有结果的HTML文件将保存在您运行命令的当前目录中。如果您在任何浏览器中打开它,您将看到类似于我们在Chrome中使用Lighthouse获得的结果。选项以下是您可以添加的一些有用选项:默认情况下,CLI工具将在宽度为360像素和高度为640像素且设备比例因子为2的移动设备上执行审核。要禁用所有这些默认值——也就是说,要在与桌面屏幕宽度和高度匹配的设备上执行审核,您可以添加选项--screenEmulation.disabled。要仅更改仿真的宽度和高度,您可以使用选项--screenEmulation.width和--screenEmulation.height。要打开报告进行查看,请在测试完成后立即添加选项--view。你可以用指定端口--port和主机名--hostname。要指定要运行的类别,请使用该选项--only-categories—例如,--only-categories=performance,seo。要指定要运行的审计,请使用选项--only-audits。要排除某些审核,请使用选项--skip-audits。要指定输出类型,请使用--output. 值可以是“json”、“html”和“csv”——例如,--output=json,html.要向执行的请求添加标头,请使用--extra-headers,其中值是一个字符串—例如,--extra-headers"{\"Accept\":\"application/json\"}"。要阻止页面中对某些URL的请求,请使用--blocked-url-patterns.如果您愿意,还有更多选项和配置【https://github.com/GoogleChrome/lighthouse#cli-options】供您查看。使用网页测试与使用Chrome或CLI相比,WebPageTest允许获得更高级和更详细的结果。虽然它不是由谷歌创建的,但它仍然可以深入分析您网站在不同场景下的性能,例如使用特定设备访问您的网站或使用较慢的互联网连接。要执行测试并生成报告,请打开WebPageTest【https://webpagetest.org/】并在文本字段中输入要测试的URL。您还可以指定测试位置,因为不同的测试位置提供您可以测试的不同设备。此外,如果向下滚动,您可以指定高级设置。在这些设置中,您可以选择不同的互联网连接。这非常有帮助,因为它可以帮助您了解您的网站如何根据不同用户(例如具有快速和慢速互联网连接的用户)执行。您还可以指定屏幕尺寸,并捕获测试视频。还有许多其他设置可让您在不同条件下测试您的网站。您可以通过在选项卡之间切换来浏览不同的设置。完成后,单击StartTest。该测试可能需要比其他方法更长的时间,因为它比我们之前所做的测试提供了更多的细节。完成后,您将首先看到我们之前指定的指标的一般结果,包括FCP、TBT、速度指数等。您还可以查看执行的每次运行的屏幕截图(默认情况下,执行三个运行,但您可以在前一个表单的高级设置中更改)。除了摘要之外,您还可以通过单击“详细信息”选项卡来查看更多详细信息。这将显示详细信息,包括每个资源加载所需的时间、加载时间、用于加载每个资源的标头等。在“性能”选项卡下,您可以查看有关图像压缩、图像格式、CDN资产等的详细信息。您可以使用这些详细信息来确定如何改进您在网站中使用的资产,从而改善网站的加载时间和性能。WebPageTest提供了更多关于您的网站分数的详细信息,以及它在使用不同设备、互联网连接速度或任何其他可能影响用户体验或网站性能的因素时的表现。这使得WebPageTest成为详细分析您的网站在不同条件下的表现的绝佳选择。对于某些开发人员来说,它可能有些高级,因此在Chrome上使用Lighthouse肯定更容易。结论提高网站的性能是开发过程中必不可少的一步。它不应该被忽视或掉以轻心。提供一个可访问且在搜索引擎中表现良好的快速网站可以增加您网站的流量,并增加注册或销售。Lighthouse是一款出色的开源工具,可提供全面的见解和分析,帮助您改进网站并吸引更多用户。...

Web前端之家推荐:提取图片中的文字工具

Web前端之家推荐两款工具【功能最全的CSS3渐变生成器和CSS三角形生成工具】后,受到大家欢迎。今天我们继续推荐另外一款工具:提取图片中的文字工具。概述要想提取图片中的文字,在平时的时候,我们可能需要手动去敲打,太麻烦了。我们今天分享的这款工具只需要您上传一张图片或者是在线图片的URL地址,就可以解析出图片上的文字。先来看下界面:功能介绍1、传图有两种方式:选择上传文件和输入线上图片URL地址。大家根据自己情况可以二选一。2、输入图片后,点击“提交”按钮后,程序会运行,左边“图片区”会显示您刚才上传的图片,然后右边会提取对应图片上的文字。3、提取的文字是根据您图片上排版进行排版,您可以直接复制文字。4、图片格式支持常用格式,例如jpg、jpeg、bmp,png等。工具演示我们目前只有PC和移动两种方式,直接打开网页地址,进行使用。工具地址:提取图片中的文字工具。Tips这款工具是响应式的,您可以PC和手机移动端随意切换,根据您自己的情况去使用。总结基本功能就这么多了,可能还有些漏洞,我们后续会慢慢修复,也会增加一些功能,如果您有什么想法和意见,都可以留言或者加入QQ群咨询。...

在国内外Web前端行业,排名前5​​的前端框架有哪些?

当今有大量的前端框架,每个都有不同的优点和缺点。这使您很难决定哪个人应该花时间在学习上,或者哪个才最适合您的下一个项目。在本文中,我将比较五个最流行的前端JavaScript框架。我将提供每种工具的高级概述,并检查它们的主要功能,工具,学习曲线以及其他要考虑的利弊。当然,我不能告诉你这是最好的框架,因为这取决于多种因素,比如你去了某个团队,需要适应他们使用的框架。我们今天分析的数据,只是从客观去衡量。如何衡量人气?流行程度是根据2020年JavaScript调查状况通过框架使用情况确定的。这项调查由23,765名受访者完成,发现竞争者像这样堆积:React:80%Angular:56%Vue.js:49%Svelte:15%Preact:13%我还考虑了同一项调查中的“框架意识”:React:100%Angular:100%Vue.js:99%Ember:88%Svelte:86%根据2020年StackOverflow开发人员调查,我将这些结果与框架使用情况进行了交叉引用。这项工作由65,000名受访者完成,并且在很大程度上证实了来自JavaScript状态的发现,然后不幸的是,它并未区分前端和后端框架。当然,还有许多其他指标可以借鉴,例如职位可用性,GitHub星号,npm下载,GitHub“Usedby”等等。我们如何定义前端框架?房间里的大象是清单(React)上最受欢迎的框架,它将自己定义为“图书馆”。我不想深入探讨这个难题,因为有完整的文章专门介绍框架和库之间的区别。出于本文的目的,我将使用MartinFowler提供的以下定义:库本质上是您可以调用的一组函数,通常组织成类。每个调用都会执行一些工作,并将控制权返回给客户端。框架体现了一些抽象设计,并内置了更多的行为。要使用它,您需要通过子类化或插入自己的类将行为插入到框架中的不同位置。然后,框架的代码在这些点上调用您的代码。在我看来,React更符合框架的行为而不是库。开发人员通常会从其生态系统中采用多种工具和软件包来使其本身发挥作用。1.React网站:https://reactjs.org/GitHub:https://github.com/facebook/react/React最初由Facebook于2013年发布,React无疑是当今最流行的前端JavaScript框架。React已被Facebook,Netflix和Airbnb等公司用于生产,它拥有大量的开发人员,这意味着很容易在网上找到帮助和资源。React的主要目的是由可重用组件组成交互式用户界面。它使用 JSX(JavaScript的语法扩展)进行模板制作,并实现了单向数据流模型以用数据填充组件。每当组件数据发生更改时,React便会使用其虚拟DOM快速有效地更新页面。开发人员工具很好。React团队已经构建并维护了一个CLI(CreateReactApp),可以快速轻松地搭建一个新项目,以及适用于Chrome和Firefox的开发人员工具扩展。有很多第三方软件包可用于完成各种各样的任务(例如,路由,处理表单和动画),以及一些基于React的框架,例如Next.js和Gatsby。React奉行“一次学习,随处编写”的理念。它可以使用ReactNative为移动应用程序提供动力,并可以使用Node在服务器上进行渲染。这意味着出色的SEO支持,只有当称为“服务器组件”的产品逐步普及时,它才会变得更好。对React的主要批评之一是它过于简单:它仅与应用程序的视图层有关,而其他一切都留给了开发人员。有些人喜欢它提供的自由,而另一些人(尤其是新开发人员)可能会因这种鼓励鼓励的React应用程序的非结构化方法而变得不知所措。React具有适度的学习曲线。它鼓励使用各种函数式编程范例(例如不变性和纯函数),这意味着开发人员在尝试构建任何重要的东西之前,最好对这些概念有一个基本的了解。如果您对React不受限制的方法感到满意,并且对开发人员留下了相当一部分开发过程的事实感到满意,那么对于任何规模的数据驱动应用程序来说,它都是一个绝佳选择。2.Angular网站:https://angular.io/GitHub:https://github.com/angular/angular/Angular是Google在前端框架空间中提供的产品。它于2010年以AngularJS(或Angular1)的形式诞生,并立即受到热捧,主要是因为它是使开发人员能够构建我们现在称为单页应用程序的第一个框架。为了解决性能问题和构建大型JavaScript应用程序的挑战,Google从头开始重新编写了AngularJS,并于2016年发布了Angular2(如今仅是Angular)。两者之间没有轻松的迁移路径,因此AngularJS和Angular成为两个独立的框架。AngularJS现在已经停产,不应该用于新项目。至于Angular,它在前端框架世界中是举足轻重的。它已由Google和Microsoft等公司在生产中使用,因此绝对经过了严格的测试。在线上也有很多资源,并且在StackOverflow上有很多与Angular相关的问题。与仅处理视图层的React不同,Angular为构建单页客户端应用程序提供了完整的解决方案。Angular组件可以实现双向数据绑定,这使它们可以侦听事件并在父组件和子组件之间同时更新值。模板是HTML的一部分,允许使用特殊语法来利用Angular的许多功能。TypeScript是Angular开发的主要语言,这使得该框架特别适合于企业级应用程序。工具很好。Angular提供了高度完善的CLI,用于初始化,开发,构建和维护Angular应用程序。还有Chrome和FirefoxDevTools扩展可用于调试Angular应用程序。Angular开箱即用,可以解决许多常见任务,例如表单和路由,但是仍然有丰富的第三方库生态系统。我认为,Angular在此处列出的所有框架中学习曲线最为陡峭。开发人员将需要熟悉TypeScript以及诸如装饰器和依赖项注入之类的概念,以便能够与该框架有效地协同工作。因此,对于新开发人员而言,这不是一个不错的选择。相反,它更适合作为团队的一部分来构建大型应用程序。如果您想全面了解React和Angular之间的差异,请参阅“ ReactvsAngular:深入比较”。3.Vue.js请注意,这些统计数据适用于Vuev2。版本3可用,但必须以安装vue@next。网站:https://vuejs.org/GitHub:https://github.com/vuejs/vue我们名单上的第三个是Vue.js,这是一个用于构建用户界面和单页应用程序的模型-视图-视图模型(MVVM)前端框架。它由EvanYou撰写,并于2014年首次发布。Vue有很多开发人员(例如,比React拥有更多GitHub明星),这可能是由于它很好地填补了这一空白。重写为Angular时由AngularJS离开。Vue由大约二十名开发人员组成的核心团队开发和维护,尽管它没有得到互联网巨头之一的直接支持,但已被阿里巴巴,Gitlab和Adobe等公司用于生产。Vue可以说是清单上所有框架中最好的文档,其论坛是获得编码问题帮助的绝佳资源。Vue在PHP世界中也很流行,并且是Laravel框架的一部分。Vue的卖点之一是,它是从头开始设计的,可以逐步采用。这意味着您可以将Vue散布到常规网页中以增强其功能,或者可以全力以赴并使用它来构建功能完善的单页应用程序。Vue.js使用基于HTML的模板语法,使您可以轻松地将属性绑定到基础数据模型。它还提供了单个文件组件,这些文件组件将模板,JavaScript代码和作用域CSS保留在同一文件中。Vue周围的工具很棒。有一个官方的CLI可以脚手架和开发Vue应用程序,并且有一个devtools扩展可用于Chrome和Firefox来帮助调试。与React形成鲜明对比的是,Vue提供了用于路由和状态管理的官方程序包,该程序包提供 了一种令人愉悦的标准化工作方式。还有各种各样的第三方工具,以及基于Vue的框架,例如Nuxt.js和Gridsome(Vue对React的Next.js和Gatsby的回答)。使用Vue的入门门槛很低,部分是由于采用增量式构建应用程序的方法,部分是由于它基于HTML,CSS和JavaScript,这是任何开发人员都应该熟悉的技术。Vue是各种规模的应用程序的绝佳选择。它适用于经验不足的开发人员,以及喜欢从其框架中获得更多结构和指导的开发人员。4.Svelte网站:https://svelte.dev/GitHub:https://github.com/sveltejs/svelte/Svelte由RichHarris于2016年发布,是框架领域的一个相对较新的成员,它采用了不同于此列表中的任何其他方法来构建Web应用程序。它的网站指出:Svelte在构建时将您的应用转换为理想的JavaScript,而不是在运行时解释您的应用代码。这意味着您无需支付框架抽象的性能成本,并且在应用首次加载时不会受到任何损失。换句话说,它避开了虚拟DOM的概念,而是倾向于在构建期间将代码编译到小型的原始JavaScript模块中,每当您的应用程序状态更改时,该模块就会更新DOM。可以想象,这使得占用空间较小的快速应用成为可能。Svelte还本地处理状态管理,并提供开箱即用的反应性。不幸的是,工具目前有点痛苦。最初,Sapper(在Svelte之上构建的应用程序框架)用于构建具有预定义结构的Svelte应用程序,并为它们配备一些更高级的功能,例如路由和服务器端渲染。但是,在2020年11月,Svelte的创建者宣布Sapper1.0版将永远不会发布,并且SvelteKit现在是开始使用Svelte构建应用的推荐方法。也有适用于Chrome和Firefox的浏览器devtools扩展,以及各种第三方模块,尽管与已建立的框架相比,扩展的数量不多。尽管Svelte的学习曲线很低,但社区规模仍然很小,并且尚未获得与此处提到的前三个框架相同的吸引力。但是它已被IBM和《纽约时报》等公司用于生产,并且绝对是在未来几个月和几年中值得关注的框架。Svelte由于其不成熟而成为小型项目的不错选择。但是,这种情况正在改变。SvelteKit处于公开测试阶段,社区正在不断发展壮大。尽管斯维尔特(Svelte)目前是一个新来者,但您应该注意这个领域……5.Ember.js在本文中,我将Ember作为最终框架,因为自从前端框架问世以来,它就已经存在了。它最初于2011年发布,但在开发人员中仍保持着持续的流行:它已有近十年的历史了,可以追溯到React,Vue,Svelte和所有其他公司之前。该框架从未出现在前端炒作的最前沿,但悄悄地使团队能够稳定,可持续地进行发布,其中包括Qonto和CLARK,这是2020年欧洲前50大金融科技公司中的两家与Angular相似,Ember在应用程序开发中采用了更多包含电池的方法,并提供了构建现代前端JavaScript应用程序所需的一切。默认范围是从路由解决方案到数据层,再到在每个应用程序中内置的功能全面的现代化测试工具。它遵循六个星期的发布周期(新的次要版本发布时),并且对稳定性具有坚定的承诺。对于那些无力承担连续重写其应用程序以避免被快速发展的框架所抛弃的开发人员而言,这可能是一个真正的优点。在Ember周围涌现了各种各样的工具,从EmberCLI(创建,构建,测试和提供Ember应用程序的官方方式)到EmberInspector(受官方支持的浏览器插件,使您可以检查Ember对象)。在您的应用程序中。还有许多可用的第三方库,并且CLI提供了一种通用格式(也称为EmberAddons)来分发它们。Ember的社区并不像React和其他社区那么大,但是它的成员参与度很高,并且拥有论坛和Discord服务器,您可以在其中寻求编码问题的帮助。Ember是此处列出的框架中最自以为是的,它采用“约定之上的配置”方法。再加上开发人员将需要熟悉更高级的概念(例如序列化器和适配器),这一事实使它具有中等至陡峭的学习曲线。对于初学者或较小的项目,Ember可能不是最佳选择。它具有许多活动部件,并且在组织事物时没有提供很大的灵活性。但是,当作为团队的一部分来构建功能丰富,复杂的前端应用程序时,它确实很出色。结论因此,我对当今市场上五个最受欢迎的前端框架进行了比较。虽然这并不是对每个框架的功能的详尽介绍,但我希望它能使您了解哪些大公司可能适合您的下一个项目,或者是您进一步探索的合适人选。...

Web前端之家推荐:功能最全的CSS3渐变生成器

好久没弄前端工具了,上次弄前端工具还是很多个月前,估计都快遗忘了,回顾下:CSS三角形生成工具不知道大家是否用过此工具呢?今天我跟大家继续分享一款实用的前端工具:功能最全的CSS3渐变生成器。首先我们来看一张界面图:看到效果图后,相信大家就知道这个工具的大概用途了,其实就是用来根据PS里的渐变来生成相关的CSS代码。在网上有很多类似的工具,但是个人觉得都不咋滴,不是功能太差,就是兼容不好,有些难用。相信了解完此款,会“爱上它”。GOGOGO!!!简介除了平时的普通功能,它还有更高级的工具,例如滴管,拾色器,调色板编辑器和网站分析器。如您所知,HTML5为Web开发人员引入了许多令人兴奋的功能。功能之一是能够使用纯CSS3指定渐变,而无需创建任何图像并将其用作渐变效果的重复背景。重要提示:您需要使用Firefox,Chrome,Safari,Opera或IE的最新版本才能使用此GradientGenerator。它们将在这些浏览器中运行,并且在较旧版本的InternetExplorer中也将退回到更简单的渐变。特征这款工具强大之处,就是几乎包含我们想要的功能,例如:强大的类似Photoshop的界面跨浏览器CSS输出水平,垂直,对角和径向渐变复杂的多级渐变不透明度支持多个不透明度十六进制,RGB,RGBA,HSL,HSLA颜色格式IE9支持完整的多级渐变从图像导入(将图像渐变转换为CSS)从现有CSS导入通过色相,饱和度,亮度调整渐变超过135个渐变预设保存自定义渐变预设Sass输出灵活的预览面板用于发送和共享的渐变永久链接帮助主渐变控件使您能够:通过拖动停止标记来调整渐变停止位置通过双击停止标记来调整停止颜色通过向上或向下拖动停止标记来删除停止通过单击现有停止标记之间的任意位置来添加新的停止您可以使用“停止”面板来更精确地控制颜色和当前所选停靠点的位置。“预览”面板允许以垂直或水平方式预览当前渐变,还可以快速预览IE中InternetExplorer后备渐变的外观。最后,“CSS”面板始终具有用于当前渐变的CSS,以便于轻松复制和粘贴到样式表中。您也可以使用此面板将现有的渐变CSS导入到该工具中。浏览器兼容性最后上下工具的地址:功能最全的CSS3渐变生成器。您也可以点击页面上导航进入工具页面:总结上述内容,只是大概陈述了一些基本知识,具体的需要大家自己慢慢研究下,其实不难。希望此款工具能够方便到大家,如果您有任何问题,都可以加QQ群讨论。...

产品经理为何要学习Web前端编码

产品经理为什么要学习编码?我们通过两人的对话方式,去了解下一位产品经理的对于”学习编码“的独特见解。您是如何学习编码的?Irma:我认为那是我上大学的最后一年。因此,我相信在2015年。2014年12月进入2015年,不知何故,我碰到了一篇文章,有人说“Check Codecademy ”,但我不知道为什么我最终还是点击了它。我当时想,“我不确定这是什么”,但是我只是单击了它。我登录了一个帐户,然后开始尝试他们所拥有的HTML课程,但它是如此不同,以至于我认为我只是被它吸引了。每次编写一行HTML时,您都会看到H1作为标题显示在右侧。而且我认为,“嘿,我只是用一种非人类的语言编写了这本书”,对,语法,立刻满足了,它最终出现在我的浏览器中。我认为这确实启发了我继续深入研究什么是编码。您使用了哪些资源来学习编码?Irma:我最终做的是,我认为Codecademy上的HTML和CSS,而且我认为这是一种永生而令人振奋的学习方式。所以我不仅说我只是要学习这两件事,还想探索不同的资源,所以我想我最终要寻找不同的资源。我想我遇到了CSS-Tricks和其他资源,就像,“好吧,让我们开发。现在让我们实际构建一些东西。我认为我在HTML上有很好的处理能力,在CSS上也有不错的处理能力。我现在不会学习全部内容,但让我们弄脏我的手。”我最终建立了自己的“关于我”Feed,这非常糟糕。我不会撒谎。回顾一下,几乎没有任何样式,H1标签在这里,那里有桌子,只有一堆标签。只是知道我从头开始构建了一些东西,真是令人满足。以前,我觉得我并没有从任何东西中得到那种东西。我当时正在上学,我看到自己的成绩还不错,但我认为我没有得到任何快乐。所以我认为编码给我带来了快乐。因此,我最终只是在一边追求这一点。下班后,我会花几个小时在深夜里,学习,编码和建造,这感觉真的非常酷。克里斯:太神奇了。因此,您创建的“关于我”页面在哪里学习如何做?它只是在谷歌上搜索还是在Codecademy上,还是其他?那确实是一个非常好的项目,实际上,这就是我们在第一周的“HTMLHTML”课程中所做的。就像制作投资组合页面一样。拥有一个很重要。首先是一个简单的项目。感觉真的很好,而且是关于你的。感觉就像,“嘿,那是我的页面”。你可以向人们展示。您为什么决定制作一个“关于我”样式的页面?那为什么是第一个很棒的HTML&CSS项目呢?Irma:我真的想说我在读东西,对。认识我自己,我可能在Google上搜索了如何处理HTML和CSS,并且我一定遇到过一篇文章,该文章只是说这些是您可以做的一些项目,只是为了入门和动手。我确实记得在YouTube上碰到过DevTips,他的课程非常好,也很连续,他只是从头开始建立自己的投资组合网站,我想我大概就完成了一半。但这是我的关于我页面的第二部分。我的第一个,我不记得我最终在哪里弄清楚了,但是我很确定这是通过Google搜索得出的。那只是您刚开始时必须做的那些项目之一。DevTips就像是我最终登陆的下一个免费资源,他的频道非常棒,因为我真的很喜欢他的个性。他没有让代码变得无聊。他只是让它变得真正有趣而又轻松,他只是在阳光下分享了您应该做的事情,不应该做的事情,在计算机上设置文件的方式等所有内容。所有这些,Codecademy都没有教给我。因此,由于Codecademy非常基于浏览器,或者至少我认为它们仍然基于浏览器。我有一段时间没有登录了。但是,是的,这就是“关于我”页面的来源,我什至不认为它最终会出现在域或任何地方的任何地方。我认为这只是本地的。我随身带着计算机,向家人展示了“这就是我的创造力”。关于使用Codecademy学习编码的想法?克里斯:我认为Codecademy是一个非常不错的开始一点点游戏的地方,您肯定可以学习。有时我会把它当作练习。这几乎就像一个工作表,在这里您只是做一些小练习,但同时也有很多学生,我会发现他们何时学会在Codecademy停止编程,因为他们看不到将其发布到下一步的下一步。真实世界。 Irma:是的。您使用什么文本编辑器(IDE)学习编码?Irma: Sublime是我的第一个IDE。克里斯:是的,所以任何人都可以从Codecademy开始,然后将所有代码放入Sublime,SublimeText。您可以使用Google应用程序启动,实际上就像是“哦,现在在我的计算机上”。Irma:是的,是的。而且我想提一提的是,不要害怕只使用Google的东西。我认为这是现在。这是既定的,对的。但我认为您可能仍会担心谷歌搜索。您可能会认为您必须与某人交谈以找出问题,实际上很多答案都在Google上。不幸的是,我周围没有开发人员或计算机科学专业的人。所以我没有人去,就像,“嘿,我想了解更多关于这件事的信息。我如何到达那里”?因此,我不得不进行搜索,当时Google是我最好的朋友,YouTube也是我最好的朋友。我想在那之后,我最终在网上发现了很少的社区。我想我例如注册了freeCodeCamp ,他们有自己的社区。但是我认为,当您刚起步时,不要害怕只使用Google。您不必有任何人或志趣相投的人。什么是freeCodeCamp?有用吗?Irma:是的。FreeCodeCamp是一种在线编码,在某种程度上,我认为它像编码训练营一样。但这几乎是您在自学。我认为他们有HTML,CSS。他们进入后端。因此,您可以学习Node,然后我认为他们现在甚至可以绕React上一课,这真的很酷。但是使用HTML会让您非常新鲜,然后可以进行很多学习,然后在浏览器的右侧进行编码。与Codecademy使用的方法非常相似,但是在freeCodeCamp上都是免费的。他们不向您收取任何费用。他们要求您捐款,但这不是一家资金驱动的公司。从我所看到的来看,这是社区驱动的,他们只是想确保世界各地的所有人都可以使用编码。因此,您真正需要的只是一个浏览器和一个互联网连接,如果您每天真的花两个小时,那么您可能会花几个月的时间学习如何编写代码。连续六个月,您可能会完成他们拥有的整个前端开发轨道。我认为我所经历的是我尝试了太多的资源,并且我认为这是很多人陷入困境的地方。回顾过去,我现在了解到的是我应该只坚持一两个。然后仅使用它们来互相补充,而不是每两个月尝试和开始新的课程。我不确定您是否从很多人那里听到了,但是,这是一件大事,我认为我做错了。确实没有错,但是我只是不知道什么更好。因此,这只是在课程之间切换而已,您知道吗?freeCodeCamp和Codecademy有什么区别?Irma:是的。它与Codecademy非常相似。据我所知,我并没有对此进行研究,但是从我使用它时,它就在您的浏览器中。HTML标记,然后您可以在右侧进行练习,或者这是您编写函数然后在右侧进行练习的方式。克里斯:哦,知道了。Irma:当您加入社区时,我想您会开始学习,例如:“好吧,我应该从浏览器中移除它,然后在本地进行练习,并了解它的感觉以及应该拥有的文件并正确处理。所有这些不同的东西。我认为,在加入他们的社区之后,freeCodeCamp所做的部分工作就是开始学习,例如,“好吧,让我们离开浏览器。做我自己的事。”克里斯:他们也有支持吗?如果您有问题,可以去那里获得问题和答案吗?艾玛:我几乎百分之一百确定他们有那个,是的。克里斯:是的。最有可能的是,我猜想就像是论坛中的社区。这种事。艾玛:对。确切地。产品经理做什么?克里斯:现在您是OpenUpResources的产品经理。产品经理做什么?Irma:产品经理做什么?[这里有个例子]假设我们使用Twitter,然后考虑一下您实际使用的产品。您下载应用程序或在线访问Twitter。不仅是工程团队,市场团队,也许是设计团队也参与其中,而且还必须有人正式制定了Twitter应该是什么的构想。那可能是很多人。那可能是首席执行官,也可能是首席技术官,但通常在下面是领导人员。有一个产品经理,产品经理从CEO和CTO那里获得了这一愿景,您基本上将其变为现实,但并非只有任何想法都能实现。一个想法必须经过很多不同的测试。因此,您必须确保有市场。因此,产品经理走出去,与人们交谈。他们了解产品是否有市场。产品经理的核心工作是确保领导者的想法或MVP愿景能够奏效。您正在接受此测试,并且实际上是对其进行了一系列测试,以验证该产品或该想法在市场上是否真的能行得通。这可以通过用户访谈,AP测试等来完成。但是,您可以做很多不同的事情来确保产品在市场上表现良好。一旦您确定该想法已得到实际验证,便可以进行工程,设计和营销。从本质上讲,您将进行协作以交付可使用的产品,并确保您的价值主张是某人下载Twitter时知道的知识,他们知道如何使用它,并知道它的用途,知道他们将获得什么价值。离它远一点。产品经理介于业务技术和通常是设计之间,他们是一个戴很多帽子的角色。每一天都是不同的。很多会议。与人们进行大量交谈并了解他们想要为产品而改变的问题和想法。但是最主要的是,您了解并非每个想法或每个解决方案都是正确的解决方案。所以我的工作是,了解我们为什么要做某事。另一端谁会受到它的影响?产品经理的“生活中的一天”?Irma:是的。让我们来看看。我可能可以回想一下,我的意思是昨天可能是一个很好的例子。所以昨天我的一天主要是开会。我早上来,检查我的日历,看看我已经排好队了。通常,这是与其他PM的会议。因此,我们有一名CTO和另一名产品经理,我们通常每周开会,讨论我们下周要开展的重要工作。这就是我们提出问题的地方。我们设置了截止日期,并查看积压的位置。那么工程正在做什么,他们正在取得什么进展呢?通常,在那之后,当我确实有大量电子邮件时,我会仔细检查一下。所以昨天,我们正在着手启动发射。我们公司的业务是印刷交付,因此学校通常从8月至5月或6月开学。而且,我们必须确保按时完成任务,这样教师才能按时收到印刷书籍,以实际阅读它们并了解他们将要教的内容,并在第一天就掌握了开始学习的内容。我们将第二次发布我们的课程之一,并且该课程将退出Beta版。我们看到人们在说什么。我们得到什么反馈,然后在第三年将其发布给公众,以便每个人都可以购买。目前,我正在创建,运行用户访谈,因此正在创建研究问题和访谈问题。我们从销售团队那里听到了什么?我们从工程学中听到了关于必须改变的事情的信息?我们从学者那里听到了什么?我们有一个称为学者的团队,因此他们从字面上专注于该课程中的内容以及它对老师的作用。本质上,我刚刚写出了这份大文件,这将使我能够与老师进行一次访谈,以了解我们正在做出的这些不同假设或它们实际上是有效的,还是我们正在假设但实际上不是的这些假设对于每天使用我们课程的教室里的人来说,这不是一个问题。昨天,我也和工程学进行了对话。我经常与他们交谈,因为我有技术方面的经验,我喜欢与他们交谈,有时我会随便打个电话给他们看,他们会告诉我他们正在处理的代码。或者,如果我喜欢,“这是什么意思?” 他们将开放他们的开发环境,并向我展示他们正在处理的代码以及每一行的作用和含义。因此,昨天我与工程团队中的一位团队成员进行了一些交谈,因为我认为与他们密切合作实际上是非常有益的。即使您不在产品管理职位上,我认为了解工程正在做什么,他们是谁也很重要。它们不仅是代码猴子。他们是真正的人,他们想要打造出出色的产品,如果您与他们合作并与他们合作,这将成为一种美好的工作关系。每个人都在协作并且真正在那里发布产品或发布另一端的人会发现有价值的功能的地方。所以这就是我的日常。昨天是所有事情的一部分。会议,电子邮件,用户面试内容以及与工程人员的一些聊天。但是,是的,每一天都是不同的。如果我谈论明天或今天,您会说:“您甚至从事相同的工作”吗?[作为产品经理]我有三个工作。是的,要戴帽子要很多,对。有时你戴着领导帽子。有时,您更像是细心的帽子。也许您正在掩盖销售团队成员并理解人们在说什么。很多,但是真的很有趣。我真的很喜欢它,因为在一天结束时,我们仍然影响着老师和学生以及他们从中学到的东西。所以这对我来说真的很重要。您是否认为学习编码有助于您找到产品经理的工作? Irma:是的。我认同。我认为,如果我不学习编码,那我就不会是今天的样子,那是一个很大的假设。我可能是错的,但不仅是我学习编码和语法,并了解了它的作用,而且还开始了解一个完整的高科技应用程序的外观,这是正确的。您有一个前端,一个后端,并且所有这些不同的语言都有自己的用途,并且已达到各自的目的。正确理解浏览器的工作方式或互联网的工作方式就可以了。这些都是我从代码中学到的,因为我对编码很感兴趣。是的,我开始只是学习HTML和CSS,但是因为我开始学习它,这促使我努力研究其他事物,并且感到好奇。因此,我认为,如果我从未尝试过或不愿意参加Codecademy,那么我希望无论哪种方式都可以,现在我可能会成为行为治疗师。在我的工作之外,不对技术做任何事情,或者对技术不做任何事情。但这真的很棒,因为现在我仍然可以成为技术人员。我可以与工程和实践人员讨论我的技能的技术方面。我可以和市场营销谈谈,而不是技术问题。翻译那里的技术语言。他们可以理解。学习如何编码也使我认识了很多人。我参加过聚会,在那里遇到了一些人,我可能永远也不会见过他们,也无法了解他们的故事以及他们的入门方式,这确实使我对技术行业敞开了眼界。我认为如果没有它,我将进入一个完全不同的行业。因此,我很高兴自己选择了Codecademy。我很高兴那时那篇文章出现在我的浏览器或手机上,并且能够对新奇有趣的事物产生兴趣。我真的不知道要带我去哪里,但是却把我带到了今天,这就是我真正要感谢的。您使用什么技术资源来保持最新状态?Irma:我通常听两个播客,其中一个叫做DeveloperTea,这真的很棒,然后我也调到Syntax.fm,它有WesBos,不记得另一个人的名字,但是他们两个都是很棒,因为他们谈论的是总体上编程和技术的当前趋势。因此,这通常是我修复代码的地方,然后我进入了几个不同的工作人员闲散工作空间,这些工作空间要么只是从事编码工作,要么已经是非常有经验的人,他们在那里就可以为他人提供真正的帮助。我认为这真的很酷。因此,例如,现在,我正在尝试为自己拥有的产品构想建立MVP,因此我们在闲置的工作空间中进行交流,就像,“嘿,我应该采取什么方法?正确的方法,还是有更好的方法来解决这个问题,而我不会为我的项目创建很多依赖项?”这些就是我要做的主要事情。然后我不时地去Verge,它是面向超级技术的,不是真正地编写太多代码,但是仍然使我对Google的工作有所了解。他们只是发布了Stadia,或者他们即将发布Stadia,诸如此类。我真的很喜欢保持联系,因为技术发展如此之快,我想:“我能跟上吗?我不知道。人工智能即将到来。” 而且您想学习所有这些东西,但是您一天只有这么多时间,所以我学会了真正选择自己想要花费的时间,因为我的时间很宝贵,我应该只花时间听或与人阅读或交谈。您想确保自己正在执行此操作,因为您想执行此操作。不要学习语言,因为乔说这是学习的正确语言。只要能够进行研究,然后再从那里去,因为时间过得很快。因此,请确保您确实在工作,并聆听您想花费时间的事情。您最喜欢哪种生产力黑客? Irma:为了提高生产力,我使用了Pomodoro Chrome扩展程序。 克里斯:啊,我也是。实际上,我们有一个视频介绍如何使用Pomodoro方法。Irma:你呢?这很棒。是的,我只是休息一下,从办公桌上站起来,因为我在家工作,所以再次流血是一件好事。它可以帮助我提醒我,“嘿,您已经工作了X倍的时间。凉爽的。起床5分钟,到处走走,喝点水,喝点咖啡,然后再回到里面”。是的,这些都是我现在想到的最重要的问题。我当时处于想要使用所有工具的阶段,但是我也减少了使用该工具的时间,因为您拥有的工具越多-克里斯:您的意思是什么,或者有一种工具叫做“每种工具”吗?Irma:哦,不。每个工具。克里斯:一次全部。Irma:是的,我曾经在那里下载过每个工具,然后就像“我需要选择”。我需要对下载的内容更加挑剔。实际上,请对其进行更多研究。是的,这些是现在想到的。克里斯:我喜欢的一个,我知道我们在谈论您的工具,但是我很想知道您是否听说过这个工具,那就是OneTab。Irma:是的,实际上。是的。克里斯:真的很简单。是的,但是这改变了我的生活,因为基本上,如果您打开了20个选项卡,则可以单击OneTab,它将把它们浓缩到一个选项卡中,并列出所有已打开的内容,因此很容易进行背部。Irma:是的。克里斯:是的。如果您使用它五分钟,您会觉得,“哦,是的。这是一个好主意”。Irma:您提出来很有趣,因为我最近改用Firefox作为我的默认浏览器,因为我喜欢“数据和隐私”。因此,我最终找到了名为Workona的工具。这个真的很酷。它与OneTab非常相似,并且几乎为浏览器创建了项目。假设您拥有一个月播客浏览器,可以创建一个名为“一个月播客”的项目,并且打开了所有不同的注释或播客页面,就像所有内容一样。然后,最重要的是,您可能拥有OneMonth课程,而OneMonth课程浏览器仅包含与您的课程相关的内容,但也许您打算下一步做什么。这真的很有趣,因为它只将所有内容保留在一个浏览器中,您可以根据要关注的内容,将选项卡切换到包含所有其他选项卡的所有其他项目。因此,在工作中,我专门针对每个即将提出的项目,所有这些不同的项目都有与此相关的文档,该文档和该文档的表格,并且仅与该项目本身相关。因此,它允许我做的只是一次专注于一个项目,而不是拥有我正在处理的30种不同事物的30个选项卡。我今天只关注X项目,所以我将只打开五个与该项目相关的选项卡的项目。所以真的很酷。我自己和另一个同事最近开始使用它,它确实在帮助我们一次只专注于一件事情。您为什么要上一个月的“学习JavaScript”课程?Irma:[课程]真的非常非常棒。我在2017年收回了该课程。正如我之前提到的,我参加了一个月的JavaScript学习课程,我想我说过:“您不可能在30天之内学会该知识”,对,但这并不是重点。我认为关键是,就像在JavaScript中站稳脚跟一样,在30天内,它确实使您能够对自己的工作更有信心。因此,关于“一个月”的最酷的事情是,我记得当我参加该课程时,是每个星期(就像一个为期四周的课程),每个星期天的星期日,您都有一个项目,而该项目几乎是建立在您的基础之上的学会了这几天。这确实对我有所帮助,因为它使我对自己所学的知识负责。所以我就像,“酷。让我们在本周学习API”。我不只是了解它,什么也不做,对。有一个我必须做的项目。它是在某个日期和某个时间到期的。因此,它产生了一种“酷”的感觉。有人将对此进行检查,而我确实需要这样做,并且如果您执行该项目,它实际上将在一天结束时为您提供帮助。” 我的意思是,确实投入了您需要付出的努力。所以真的很棒。我绝对会向有兴趣学习JavaScript的任何人推荐它。即使您是一个超级入门者,它也从一开始就包含在您需要了解的内容中,并且讲师很棒。我真的很喜欢您的作品被分级的事实,如果有问题,我可以将它们发送到Slack,那里有一个社区可以提供帮助。是的,我真的很喜欢。我认为你们在这门课程上做得非常出色如果您没有学习编码的经验,那么您认为哪种语言应该是开始使用的第一语言?Irma:如果您没有经验?就像您从未听说过HTML和CSS一样,对吧?是的,我肯定会从[HTML和CSS开始],并且我也建议人们像谷歌一样研究“互联网如何工作?“正确的。因为这样可以为您提供许多您通常不了解的信息,这些信息通常涉及浏览器的工作方式,浏览器如何处理代码以及互联网的工作方式。我认为它经历了HTTP协议,以及它如何与服务器一起工作,然后经历了面向计算机的爬升过程以及它们之间的所有连接方式,以及如何将一件事授予另一件事以将页面加载到浏览器上。因此,我认为这确实可以帮助人们真正建立联系,例如“酷。我不仅在编写HTML和CSS。” 我的代码在后台发生了其他事情,我看不到,因为这是构建的,我不知道多少年前,这一切都发生了,这就是为什么事情可以加载到页面上的原因,这就是HTML加载到页面以及CSS加载到页面的方式。因此,绝对我的建议是从了解互联网的工作原理,浏览器的工作原理开始,然后同时涉足HTML和CSS。当然。克里斯:太好了。凉爽的。好,非常感谢您今天加入我们的节目。与您交谈真是太好了。总结一专多长,你才会比别人更优秀。...

Web前端开发:无服务器功能及其部署指南

在过去的几年中,无服务器功能(有时也称为“无服务器”或“无服务器计算”)已成为一种流行的技术。但是,这个词仍然有很多困惑。没有服务器如何运行代码?该技术的优缺点是什么?您可能在什么情况下使用它?在本文中,我希望回答这些问题,并为您提供有关该技术的良好概述。什么是无服务器功能?第一次听到“无服务器”一词肯定会引起好奇。“如何在没有服务器的情况下在网络上运行代码?” 你可能想知道。实际上,这意味着作为开发人员,您不必担心代码在其上运行的服务器。无服务器提供者将硬件的供应,配置网络,安装软件和扩展都抽象化了。从开发角度来看,无服务器功能是您上传到无服务器提供程序(例如AWS或Google)的一捆代码。可以将该代码配置为通过URL响应请求,按计划运行(即通过cron作业)或从其他服务或无服务器功能调用。无服务器功能非常适合在前端应用程序中添加一些后端功能,而又无需运行完整服务器的复杂性和成本。另一方面,您还可以使用无服务器功能构建整个应用程序。结合提供文件存储,数据库系统和身份验证的其他云服务,可以构建大型,健壮和可扩展的应用程序,而无需配置单个服务器。无服务器功能的优点无服务器功能在按需启动的微型容器中运行。它们是为运行时间较短的流程而设计的,因此记帐时要牢记这一点。与通常按小时计费的完整服务器实例不同,无服务器功能通常按GB-秒计费。以毫秒为单位的最小计费持续时间,低频或零星的工作负载作为无服务器功能运行比传统服务器实例便宜得多。轻量级工作负载和原型制作甚至可以属于某些提供商的免费套餐。无服务器功能的按需调用意味着它们可以快速轻松地扩展,而无需开发人员方面的额外工作。这使它们成为流量不可预期的高峰的理想选择,因为将自动提供更多功能实例来处理负载。之后,该功能将按比例缩小,这意味着您无需为未使用的容量付费。无服务器模型的主要优点是不必处理服务器。运行Web应用程序需要大量时间和服务器管理方面的专业知识,才能使软件与最新的安全补丁保持同步,并确保正确配置服务器以确保其安全性和性能。对于初创企业和小型企业,雇用人员来处理服务器管理是一笔很大的额外开销。借助无服务器,开发人员可以专注于创建解决方案。无服务器功能的缺点当然,没有技术是完美的,无服务器功能并非没有缺点。正如我之前提到的,无服务器模型在设计时考虑到了短暂的过程。以分钟为单位的最大执行时间(例如,在AWS上为15,在Google上为9),它不适合长时间运行的工作,例如处理大量数据。另一个广泛讨论的问题是冷启动时间。这是提供商在准备好开始运行之前为无服务器功能预配和初始化容器所花费的时间。一旦函数完成运行,如果再次执行代码,则容器将保留很短的时间以备重用。这种“冷启动”延迟可能会使功能的响应时间增加半秒到一秒。有一些解决方法,包括Serverless框架的WarmUp插件,该插件按计划ping您的函数以使容器保持活动状态。尽管无服务器功能使您不必担心服务器配置和维护,但这并不是说没有学习曲线。使用无服务器构建应用程序需要与使用传统的单片代码库不同的思维方式。您必须以不同的方式来构造代码,将功能分解为更小的,离散的服务,以适应无服务器功能的约束。部署也更加复杂,因为每个功能都是独立版本和更新的。还存在供应商锁定的问题,这有时被称为无服务器技术的不利方面。按照目前的情况,该领域的主要提供商(AWS,Google,Azure)具有各自不同的实现和管理工具。这可能使将无服务器应用程序从一个云提供商迁移到另一云提供商变得困难。诸如无服务器框架之类的项目已尝试抽象化基础服务,以使应用程序在提供程序之间可移植。无服务器功能用例尽管无服务器功能可以用于构建整个应用程序,但让我们看一些不那么雄心勃勃的用例,其中无服务器可以使普通开发人员受益。表格邮件除了用户希望在用户点击“发送”时通过电子邮件将其发送给客户的联系方式之外,网站完全是静态的并不少见。该站点的托管提供程序可能支持也可能不支持服务器端脚本,即使如此,它也可能不是您所熟悉的语言。通过将无服务器功能设置为表单邮件程序,可以将功能添加到静态主机上的站点。Cron工作有时您可能需要在后台运行计划任务。通常,您必须为一台服务器付费才能设置cron作业,而该服务器将在两次作业之间处于空闲状态。借助无服务器功能,您只需支付作业的运行时间(如果属于免费套餐,则可能根本不用花钱)。缩略图生成器想象一下,您的React应用程序允许用户上传照片,以在整个应用程序中用作头像。您想调整上传图像的大小,以免通过提供远远超出所需大小的图像来浪费带宽。可以使用无服务器功能来处理上传请求,将图像调整为所需的大小,然后保存到诸如S3或GoogleStorage之类的服务中。无服务器功能的实际示例为了更深入地了解如何使用无服务器功能,我们来看一个真实的示例。我们将创建一个带有新闻简报注册表单的静态页面,该页面使用无服务器功能将用户名和电子邮件地址保存到Google电子表格中。取决于提供程序,无服务器功能可以用多种语言编写,但是我们将使用JavaScript,因为Netlify支持Node.js函数。我将假设您已经在本地计算机上安装了最新版本的Node/npm,以便进行后续操作。1.注册一个Netlify帐户在本示例中,我们将使用Netlify作为主机,因为它们提供了包括无服务器功能的免费层,并且非常容易启动和运行。首先,跳至他们的网站并注册一个免费帐户[https://app.netlify.com/signup]。2.安装NetlifyCLI工具为了在本地测试示例站点并部署到Netlify,我们将使用其CLI工具。可以从命令行将其作为全局npm模块安装:npm install -g netlify-cli安装CLI后,运行以下命令将打开浏览器窗口,以将CLI连接到您的帐户:netlify login3.创建一个项目文件夹并安装依赖项让我们为项目创建一个文件夹,并初始化一个新的npm项目:mkdir serverless-mailinglist && cd serverless-mailinglistnpm init -y这将为我们设置package.json项目文件,准备安装依赖项。说到这,我们将需要几个软件包来实现我们的无服务器功能:npm install dotenv google-spreadsheet第一个是dotenv,它是一个软件包,该软件包可让我们从.env项目根目录中的文件中加载值,并将其公开给Node脚本(我们的无服务器功能),就好像它们是环境变量一样。另一个是google-spreadsheet,这是一个包装了GoogleSheetsAPI并易于使用的软件包。4.启用GoogleSheetsAPI并创建凭据为了使用SheetsAPI,我们需要做一些准备工作。首先,您需要转到API控制台来为您的Google帐户启用API。从顶部的菜单中创建一个新项目,然后单击“启用”按钮。完成后,您将需要创建一个服务帐户。此帐户将为您提供一组凭据,这些凭据具有访问API所需的权限。为此,请按照下列步骤操作:确保您位于SheetsAPI管理屏幕上。单击左侧边栏中的凭据,然后单击+创建凭据,然后从下拉列表中选择服务帐户。填写表格,为服务帐户选择一个名称。您选择的名称加上项目名称将成为服务帐户ID的一部分。例如,如果您将帐户命名为“MailingList”,而项目名称是“SitepointServerlessDemo”,则ID类似于mailing-list@sitepoint-serverless-demo.iam.gserviceaccount.com。点击创建。您可以跳过页面上其余的两个可选部分。单击继续,然后单击完成。接下来,单击新创建的服务帐户。这将带您到一个显示帐户详细信息的屏幕。点击顶部菜单中的KEYS,然后点击AddKey和Createnewkey。选择JSON作为密钥类型。单击CREATE按钮,然后JSON密钥文件将下载到您的计算机。(注意:这是唯一的副本,因此请确保安全!)5.创建注册表单页面让我们继续创建一个简单的注册页面,该页面将允许用户将其详细信息提交到我们的邮件列表。index.html在项目根目录中创建一个文件,内容如下:<!DOCTYPE html><html lang="en">  <head>    <meta charset="utf-8">    <title>Sign Up For Beta Form</title>    <link rel="stylesheet" href="style.css">    <link href='https://fonts.googleapis.com/css?family=Lato:400,700' rel='stylesheet' type='text/css'>  </head>  <body>    <form action="/.netlify/functions/subscribe" method="post">      <div class="header">         <p>Get Great Content Every Week</p>      </div>      <div class="description">        <p>I publish new articles every week. Be sure to subscribe to my newsletter to make sure you never miss a post!</p>      </div>      <div class="input">        <input type="text" class="button" id="name" name="name" placeholder="YOUR NAME">      </div>      <div class="input">        <input type="text" class="button" id="email" name="email" placeholder="NAME@EXAMPLE.COM">        <input type="submit" class="button" id="submit" value="SIGN UP">      </div>    </form>  </body></html>和一个style.css文件,具有以下规则:body {  background: #A6E9D7;  font-family: 'Lato', sans-serif;  color: #FDFCFB;  text-align: center;  background-image: url(https://images.pexels.com/photos/326311/pexels-photo-326311.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940)}form {  width: 450px;  margin: 17% auto;}.header {  font-size: 35px;  text-transform: uppercase;  letter-spacing: 5px;}.description {  font-size: 14px;  letter-spacing: 1px;  line-height: 1.3em;  margin: -2px 0 45px;}.input {  display: flex;  align-items: center;}.button {  height: 44px;  border: none;}#email {  width: 75%;  background: #FDFCFB;  font-family: inherit;  color: #737373;  letter-spacing: 1px;  text-indent: 5%;  border-radius: 5px 0 0 5px;}#name {  width: 100%;  background: #FDFCFB;  font-family: inherit;  color: #737373;  letter-spacing: 1px;  text-indent: 5%;  border-radius: 5px;  margin-bottom: 1em;}#submit {  width: 25%;  height: 46px;  background: #E86C8D;  font-family: inherit;  font-weight: bold;  color: inherit;  letter-spacing: 1px;  border-radius: 0 5px 5px 0;  cursor: pointer;  transition: background .3s ease-in-out;}#submit:hover {  background: #d45d7d;}input:focus {  outline: none;  outline: 2px solid #E86C8D;  box-shadow: 0 0 2px #E86C8D;}6.创建一个无服务器功能来处理表单现在我们有了表单,我们需要为无服务器功能创建代码,该代码将处理POST请求,并通过API将数据保存到Google电子表格中。为了使Netlify部署我们的功能,我们必须遵循它们的命名约定,并netlify/functions/在我们的项目文件夹中创建文件夹路径。在新功能文件夹中,创建一个JavaScript文件subscribe.js:if (!process.env.NETLIFY) {  require('dotenv').config();}const { parse } = require('querystring');const { GoogleSpreadsheet } = require('google-spreadsheet');exports.handler = async (event, context) => {  const doc = new GoogleSpreadsheet(process.env.GOOGLE_SPREADSHEET_ID_FROM_URL);  await doc.useServiceAccountAuth({    client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,    private_key: process.env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n')  });  await doc.loadInfo();  const sheet = doc.sheetsByIndex[0];   try {    if (event.httpMethod === 'POST') {      /* parse the string body into a useable JS object */      const data = parse(event.body);      await sheet.addRow(data);      return {        statusCode: 302,        headers: {          Location: '/success.html'        }      };    } else {      return {        statusCode: 500,        body: 'unrecognized HTTP Method, must be POST'      };    }  } catch (err) {    console.error('error ocurred in processing ', event);    console.error(err);    return {      statusCode: 500,      body: err.toString()    };  }};注意:该功能代码改编自具有NetlifyDev的博客文章GoogleSheetsv4API【https://www.swyx.io/netlify-google-sheets/】。Netlify的默认配置意味着该netlify/functions路径下的JavaScript文件可以在/.netlify/functions/URL(注意之前的句点netlify)加上文件名减去扩展名后调用。该文件netlify/functions/subscribe.js将在相对URL上可用/.netlify/functions/subscribe。基于节点的无服务器功能的基本要求是导出一个处理程序功能,该功能将在端点接收到请求时被调用。该函数传递两个参数。该event参数提供对请求详细信息的访问,例如标头和HTTP方法。通过该context参数,可以访问有关调用函数的上下文的信息,例如,包括已认证用户的详细信息。函数代码本身使用提供的凭据连接到GoogleSheetsAPI。然后,它解析请求正文,并通过API将提交的名称和电子邮件地址添加到电子表格中。完成后,该函数将返回302响应,以将用户重定向到成功页面。(创建此页面留给读者完成。)为了能够在本地测试该功能,我们需要.env在项目根目录中创建一个文件,并添加一些变量:GOOGLE_SERVICE_ACCOUNT_EMAIL=mailing-list@sitepoint-serverless-demo.iam.gserviceaccount.comGOOGLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANB \\etcGOOGLE_SPREADSHEET_ID_FROM_URL=1N8afdqnJjBhwXsvplIgU-5JoAFr3RapIrAS9oUybFnU服务帐户电子邮件是您在步骤4中创建的电子邮件,私钥来自您下载的JSON密钥文件。最后一个,即电子表格ID,我们将在下一步中获取。7.创建电子表格并共享转到Google表格并创建一个新的电子表格。赋予它什么标题都没有关系,但是请记下URL中的ID,并将其添加到.env上一步中创建的文件中。在电子表格的第一行中,添加两个列标题:name和email(请注意,区分大小写与HTML表单中的输入名称匹配非常重要)。由无服务器功能创建的条目将作为附加行添加在此下方。现在,您必须授予创建的服务帐户访问电子表格的权限。单击共享按钮,然后在输入框中输入服务帐户的电子邮件地址。确保分配编辑者权限。8.使用NetlifyCLI在本地进行测试NetlifyCLI工具的一个不错的功能之一是,它允许您在发布到他们的服务之前在本地测试代码。要启动开发服务器,请运行以下命令:netlify dev一个新的浏览器选项卡将自动打开,并显示该站点。填写并提交表单将运行无服务器功能(本地提供),然后在成功时重定向浏览器。如果您跳至Google表格上的电子表格,则应该在新行中看到输入的详细信息。9.部署到NetlifyCLI工具在模拟计算机上本地运行的Netlify服务方面做得很好,但是如果您想查看项目在其服务器上运行,则还可以使用CLI发布项目。运行以下命令:netlify deploy然后按照提示进行操作。您的站点(包括无服务器功能)将发布到Web上。不要忘记,您还需要设置环境变量以镜像.env文件中的那些变量。您可以从Netlify网站的管理面板或通过CLI工具进行设置:netlify env:set VAR_NAME value无服务器:只是时尚,还是后端的未来?同时,无服务器已成为时尚,并被誉为后端应用程序的未来。自2014年以来,亚马逊的Lambda功能已经存在,并且是AWS的重要产品。当然,在许多情况下,仍需要运行24/7并具有完全shell访问权限的实际服务器的灵活性和功能。但是,正如我们已经看到的,对于某些类型的工作负载,无数的廉价成本,可伸缩性和低维护优势使其成为一个不错的选择。随着无服务器生态系统中书籍,课程,框架和服务的增加,可以肯定的是,无服务器功能将长期存在。...

Web前端开发Tips:实现一个简单的留言本功能

Web前端开发小应用:实现一个简单的留言本功能。先看看效果图如下:DEMO代码如下:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Web前端开发Tips:实现一个简单的留言本功能-Web前端之家/https://www.jiangweishan.com</title><style>*{margin:0;padding:0;}body{padding:100px;}.box{width:80%;margin:50pxauto;}h2{text-align:center;}textarea{width:94%;height:100px;border:1pxsolid#ccc;outline:none;resize:none;padding:10px3%;font-size:14px;color:#666;}.ulist{margin-top:10px;}.ulistli{list-style:none;padding:5px;background-color:#f5f5f5;color:#333;font-size:14px;margin:5px0;}h4{margin-top:30px;}.ulistlia{float:right;text-decoration:none;}.boxbutton{margin-top:10px;display:block;border:none;width:100%;line-height:40px;background:#84bef3;color:#fff;}.item{margin-top:30px;}</style></head><body><divclass="box"><h2>Web前端之家-留言本</h2><divclass="item"><textareaname=""id=""></textarea><button>发布</button></div><h4>留言列表:</h4><ulclass="ulist"></ul></div><scripttype="text/javascript">varbtn=document.querySelector('button')vartextarea=document.querySelector('textarea')varul=document.querySelector('ul')btn.onclick=function(){if(textarea.value==''){alert('没有输入内容~~~~')returnfalse}else{varli=document.createElement('li')li.innerHTML=textarea.value+"<ahref='javascript:;'>删除</a>"ul.insertBefore(li,ul.children[0])varas=document.querySelectorAll('a')for(vari=0;i<as.length;i++){as[i].onclick=function(){ul.removeChild(this.parentNode)}}}}</script></body></html>试试吧,您需要需要更多的功能,可以在这个上面进行优化哟。...

Web前端开发:了解下微型前端架构的入门指南

现代Web开发可提供丰富的用户体验,涵盖了用户流和交互的范围。建立,维护,部署和交付这些体验需要大规模的开发团队和复杂的部署系统。Web应用程序的当前状态用于现代Web应用程序的最常见模式是单页应用程序(SPA)。SPA的核心原理是构建交付给用户的单个Web应用程序。SPA通过根据用户交互或数据更改来重写页面内容来工作。SPA通常将包含用于处理页面导航和深层链接的路由器,并且可以由多个组件(例如购物篮或产品列表)组成。典型的SPA应用流程遵循标准步骤:用户访问Web应用程序浏览器请求JavaScript和CSSJavaScript应用程序启动,并将初始内容添加到浏览器文档中用户与应用程序进行交互-例如单击导航链接或将产品添加到购物篮该应用程序重写了部分浏览器文档以反映更改在大多数情况下,使用JavaScript框架可实现上述目的。React,Vue或Angular等框架具有可帮助构建SPA的模式和最佳实践。作为示例,React是一个非常直观的框架,使用JSX来根据用户和数据更改呈现内容。让我们看下面的基本示例://App.jsimport React from "react";import "./styles.css";const App = () => { return (   <div className="App">     <h1>Hello I'm a SPA.</h1>   </div> );}export default App;这是我们的基本应用。它呈现了一个简单的视图:import React from "react";import ReactDOM from "react-dom";import App from "./App";const rootElement = document.getElementById("root");ReactDOM.render( <React.StrictMode>   <App /> </React.StrictMode>, rootElement);接下来,我们通过将React应用程序呈现到浏览器DOM中来启动应用程序。这只是SPA的基础。从这里,我们可以添加更多功能,例如路由和共享组件。SPA是现代开发的主要内容,但并不完美。SPA有很多缺点。其中之一是搜索引擎优化的损失,因为只有在用户在浏览器中查看应用程序后,才会呈现该应用程序。Google的网络爬虫将尝试呈现页面,但无法完全呈现应用程序,并且您将失去攀登搜索排名所需的许多关键字。框架复杂性是另一个缺点。如前所述,有许多框架可以提供SPA体验,并允许您构建可靠的SPA,但是每个框架都针对不同的需求,因此很难知道采用哪种框架。浏览器性能也可能是一个问题。因为SPA执行用户交互的所有呈现和处理,所以它可能具有连锁效应,具体取决于用户的配置。并非所有用户都会在现代浏览器上通过高速连接运行您的应用程序。为了保持流畅的用户体验,需要减小捆绑包的大小并尽可能减少客户端上的处理。以上所有这些导致最终的问题,即规模。试图构建一个可以满足用户所有需求的复杂应用程序需要多个开发人员。使用SPA可能会导致许多人使用相同的代码来尝试进行更改并引起冲突。那么所有这些问题的解决方案是什么?微型前端!什么是微型前端?微型前端是一种架构模式,用于构建可扩展的Web应用程序,该应用程序随您的开发团队一起增长,并允许您扩展用户交互。我们可以说这是我们SPA的简化版本,从而将其与现有SPA关联起来。对于用户而言,此版本仍然看起来像SPA,但在幕后,它会根据用户的流动态加载应用程序的某些部分。为了进一步说明这一点,让我们以比萨店应用程序为例。核心功能包括选择披萨,然后将其添加到您的购物篮中并签出。以下是该应用程序的SPA版本的模型。通过考虑可以拆分的应用程序的不同部分,让我们将其变成一个微型前端。我们可以按照分解创建应用程序所需的组件时的方式来考虑。所有微型前端都从宿主容器开始。这是将所有部分结合在一起的主要应用程序。这将是访问应用程序时发送给用户的主要JavaScript文件。然后,我们转到实际的微型前端-产品列表和购物篮前端。这些可以在本地与主要主机分离,并作为微型前端交付。让我们更深入地研究“与主机分离的本地”。当我们想到传统的SPA时,在大多数情况下,您将构建一个JavaScript文件并将其发送给用户。使用微型前端,我们仅将主机代码发送给用户,并且根据用户流,我们进行网络调用以获取应用程序其余部分的其他代码。该代码可以与启动主机存储在不同的服务器上,并且可以随时更新。这将导致更多的生产开发团队。如何建立一个微型前端?有多种构建微型前端的方法。对于此示例,我们将使用webpack。Webpack5发布了模块联合作为核心功能。这使您可以将远程Webpack构建导入到您的应用程序中,从而为微型前端提供易于构建和维护的模式。完整的webpack微型前端应用程序可以在这里[https://github.com/sitepoint-editors/webpack-federation-example]找到。HomeContainer首先,我们需要创建一个将成为应用程序宿主的容器。这可以是应用程序的非常基本的框架,也可以是在用户与产品进行交互之前具有菜单组件和一些基本UI的容器。使用webpack,我们可以导入ModuleFederation插件并配置容器和任何微型前端:// packages/home/webpack.config.jsconst ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");module.exports = {  ...  plugins: [    new ModuleFederationPlugin({      name: "home",      library: { type: "var", name: "home" },      filename: "remoteEntry.js",      remotes: {        "mf-products": "products",        "mf-basket": "basket",      },      exposes: {},      shared: require("./package.json").dependencies,    }),    new HtmlWebPackPlugin({      template: "./src/index.html",    }),  ],};注意:您可以查看webpack.config.js文件在GitHub上这里[https://github.com/sitepoint-editors/webpack-federation-example/blob/master/packages/home/webpack.config.js]。在这里,我们将模块命名为“home”,因为这是容纳所有前端的容器。然后,我们提供库详细信息,因为容器也可以是微型前端,因此我们声明有关它的详细信息,例如其类型,在这种情况下为var。类型定义了它是哪种webpack模块类型。var声明该模块是ES2015兼容模块。然后,我们将产品和购物篮模块设置为遥控器。这些将稍后在导入和使用组件时使用。将模块导入应用程序时,将使用我们提供的模块名称(“mf-products”和“mf-basket”)。配置模块后,可以将脚本标签添加到主index.html目录的主文件中,该文件将指向托管的模块。在我们的例子中,它们都在localhost上运行,但是在生产环境中,它可以在Web服务器或AmazonS3存储桶上。<!-- packages/home/src/index.html --><script src="http://localhost:8081/remoteEntry.js"></script> //product list<script src="http://localhost:8082/remoteEntry.js"></script> //basket注意:您可以查看index.html文件在GitHub上这里[https://github.com/sitepoint-editors/webpack-federation-example/blob/master/packages/home/src/index.html]。家用容器的最后一部分是导入和使用模块。在我们的示例中,模块是React组件,因此我们可以使用React.lazy导入它们,并像对待任何react组件一样使用它们。通过使用React.lazy我们可以导入组件,但是仅在呈现组件时才获取基础代码。这意味着即使用户不使用它们,我们也可以导入它们,并在有条件后有条件地渲染它们。让我们看一下如何使用组件:// packages/home/src/src/App.jsxconst Products = React.lazy(() => import("mf-nav/Products"));const Basket = React.lazy(() => import("mf-basket/Basket"));注意:您可以查看App.jsx文件在GitHub上这里[https://github.com/sitepoint-editors/webpack-federation-example/blob/master/packages/home/src/App.jsx#L4]。与标准组件用法的主要区别在于React.lazy。这是一个内置的React函数,用于处理代码的异步加载。正如我们过去在使用React.lazy代码时获取代码一样,我们需要将组件包装在Suspense组件中。这有两件事:触发组件代码的获取,并呈现正在加载的组件。除了Suspense组件和fallback组件,我们可以像其他任何React组件一样使用我们的微型前端模块。产品[ProductandBasket]配置家用容器后,我们需要设置产品和购物篮模块。这些遵循与家用容器类似的模式。首先,我们需要导入webpackModuleFederation插件,就像在home容器的webpack配置中所做的那样。然后我们配置模块设置:// packages/basket/webpack.config.jsconst ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");module.exports = {  ...  plugins: [      new ModuleFederationPlugin({        name: 'basket',        library: {          type: 'var', name: 'basket'        },        filename: 'remoteEntry.js',        exposes: {          './Basket': './src/Basket'        },        shared: require('./package.json').dependencies      })  ],};我们为模块提供一个名称,该名称将是产品或购物篮以及库的详细信息,然后是fileName-在这种情况下为远程输入。这是webpack的标准,但可以是您想要的任何东西-例如产品代码名称或模块名称。这将是webpack生成的文件,并将托管给宿主容器以供参考。使用fileNameremoteEntry,模块的完整URL将为http://myserver.com/remoteEntry.js。然后,我们定义暴露选项。这定义了模块导出的内容。在我们的例子中,这只是购物篮或产品文件,这是我们的组件。但是,这可能是多个组件或不同的资源。最后,回到家庭容器中,这就是使用这些组件的方式:// packages/home/src/src/App.jsx<div className="app-content">  <section>    <React.Suspense fallback={<div>....loading product list</div>}>      <ProductList        onBuyItem={onBuyItem}      />    </React.Suspense>  </section>  <section>    {      selected.length > 0 &&      <React.Suspense fallback={<div>....loading basket</div>}>        <Basket          items={selected}          onClear={() => setSelected([])}        />      </React.Suspense>    }  </section></div>注意:您可以查看ProductandBasketusage文件在GitHub上这里[https://github.com/sitepoint-editors/webpack-federation-example/blob/master/packages/home/src/App.jsx#L23]。依存关系[Dependencies]我们还没有谈论依赖性。如果您从上述代码示例中注意到,则每个webpack模块配置都有一个共享的配置选项。这告诉webpack在微型前端之间应该共享哪些Node模块。这对于减少最终应用程序上的重复非常有用。例如,如果basket和home容器都使用样式化的组件,则我们不想加载两个版本的样式化组件。您可以通过两种方式配置共享选项。第一种方法是作为您要共享的已知共享节点模块的列表。另一个选项是从其自己的程序包JSON文件中提供模块依赖项列表。这将共享所有依赖关系,并且在运行时webpack将确定所需的依赖项。例如,当导入购物篮时,webpack将能够检查其需求以及是否已共享其依赖项。如果购物篮使用Lodash,但家不使用,则它将从购物篮模块获取Lodash依赖项。如果房屋已经有Lodash,则不会加载。缺点所有这些听起来都很棒-太好了,难以置信。在某些情况下,这是完美的解决方案。在其他情况下,这可能会导致超出其价值的开销。尽管微型前端模式可以使团队更好地合作,并在应用程序的各个部分上快速前进,而不会因繁琐的部署管道,混乱的Git合并和代码审查而减慢速度,但仍有一些缺点:复制依赖逻辑。如依赖性部分所述,webpack可以为我们处理共享的Node模块。但是,当一个团队使用Lodash来实现其功能逻辑而另一个团队使用Ramda时会发生什么呢?现在,我们提供了两个功能编程库,以实现相同的结果。设计,部署和测试的复杂性。现在,我们的应用程序可以动态加载内容,因此很难完整了解整个应用程序。确保跟踪所有微型前端本身就是一项任务。部署可能会变得更加危险,因为您不确定100%在运行时将什么内容加载到应用程序中。这导致更难的测试。每个前端都可以单独进行测试,但是需要获得完整的,真实世界的用户测试,以确保该应用程序适合最终用户。标准。现在,应用程序被分解成较小的部分,很难使所有开发人员都遵循相同的标准。一些团队可能比其他团队进步更多,或者提高或降低代码质量。将所有人保持在同一页面上对于提供高质量的用户体验很重要。成熟:微前端不是一个新概念,并且在使用iframe和自定义框架之前已经实现。但是,webpack直到最近才将其作为webpack5的一部分引入。对于webpack捆绑,它仍然是新事物,并且有很多工作可以建立标准并使用这种模式发现错误。要使它成为一种强大的,可立即投入生产的模式,还有很多工作要做,可以由使用webpack的团队轻松使用。结论因此,我们学习了如何使用webpack模块联合来构建React应用程序以及如何在微前端之间共享依赖项。这种构建应用程序的模式非常适合团队将应用程序分解为较小的部分,从而与传统的SPA应用程序(部署和发布过程较慢)相比,可以更快地增长和升级。显然,这并不是可以应用于所有用例的灵丹妙药,但是在构建下一个应用程序时需要考虑这一点。由于一切仍然很新,我建议您尽早采用微型前端以进入地面,因为从微型前端模式转换为标准SPA比其他方法更容易。...

Web前端开发每日TIPS:Javascript节流和防抖函数

Web前端开发TIPS:Javascript节流和防抖函数。这个功能我们经常会用到,尤其是在面试过程中,考官一般会问的。接下来,我们一起来了解下。1.节流函数throttle// 节流方案1,每delay的时间执行一次,通过开关控制function throttle(fn, delay, ctx) {  let isAvail = true  return function () {    let args = arguments // 开关打开时,执行任务     if (isAvail) {      fn.apply(ctx, args)      isAvail = false // delay时间之后,任务开关打开       setTimeout(function () { isAvail = true }, delay)    }  }}// 节流方案2,通过计算开始和结束时间function throttle(fn,delay){      // 记录上一次函数出发的时间      var lastTime = 0      return function(){      // 记录当前函数触发的时间      var nowTime = new Date().getTime()      // 当当前时间减去上一次执行时间大于这个指定间隔时间才让他触发这个函数      if(nowTime - lastTime > delay){        // 绑定this指向        fn.call(this)        //同步时间        lastTime = nowTime      }      } }2.防抖debounceTail2.1)只执行首次// 防抖 且首次执行// 采用原理:第一操作触发,连续操作时,最后一次操作打开任务开关(并非执行任务),任务将在下一次操作时触发)function debounceStart(fn, delay, ctx) {  let immediate = true  let movement = null  return function() {    let args = arguments         // 开关打开时,执行任务    if (immediate) {      fn.apply(ctx, args)      immediate = false    }    // 清空上一次操作    clearTimeout(movement)         // 任务开关打开    movement = setTimeout(function() {      immediate = true    }, delay)  }}2.2)只执行最后一次// 防抖 尾部执行// 采用原理:连续操作时,上次设置的setTimeout被clear掉function debounceTail(fn, delay, ctx) {  let movement = null  return function() {    let args = arguments         // 清空上一次操作    clearTimeout(movement)         // delay时间之后,任务执行    movement = setTimeout(function() {      fn.apply(ctx, args)    }, delay)  }}...

Web前端开发:聊聊input输入框全角和半角应用

Web前端开发:聊聊input输入框全角和半角应用 - Web前端之家https://www.jiangweishan.com</title></head> <body>        <input  type="text" onkeyup="checkBanj(this);">    <script>        function checkBanj(obj){            var str=obj.value;            var result="";            for (var i = 0; i < str.length; i++){                if (str.charCodeAt(i)==12288){                    result+= String.fromCharCode(str.charCodeAt(i)-12256);                    continue;                }                if (str.charCodeAt(i)>65280 && str.charCodeAt(i)<65375)                result+= String.fromCharCode(str.charCodeAt(i)-65248);                else result+= String.fromCharCode(str.charCodeAt(i));            }            obj.value=result;        }    </script></body> </html>试试吧!!!!...

Web前端开发:Array数组类型详解

在ECMAScript中除了object类型之外,Array数组用的是最常用的类型。ECMAScript数组可以在每一项存储任何类型的值,无需指定数组的长度,还可以随着数据的增长来增加数组长度,这些是和其他语言的数组不同的。1.数组的创建方法数组字面量方式var arr = [1,2,3,4,5];// 简单直接用中括号包裹构建数组数组构造函数var arr = new Array(1,2,3,4,5);// 通过内置Array对象构建数组2.检测数组instanceof操作符,在全局环境下可以检测对象是否为数组,但是如果页面上存在多个框架时就会存在两种以上的全局环境,这时候这种方法就有些问题。console.log(arrinstanceofArray);//trueArray.isArray()方法就解决了上面的问题,可以快速的检测对象是否为数组。console.log(Array.isArray(arr));//true3.转换方法使用toString()方法可以使数组返回字符串。var arr = [1,2,3,4];console.log(arr.toString()); // 1,2,3,4toLocaleString()方法也可以实现var arr = [1,2,3,4];console.log(arr.toLocaleString()); // 1,2,3,44.栈方法ECMAScript为数组提供了一种类似于其他语言数组的方法(栈方法),栈是一种数据结构,主要讲究先进后出;push()方法向数组末尾添加元素(可以添加多项)pop()方法向末尾删除最后一项(一次只能删除一项)var arr = [1,2,3,4];arr.push(5,6,7); // 向末尾添加5,6,7console.log(arr); //输出[1,2,3,4,5,6,7]arr.pop(); // 删除最后一项console.log(arr); //输出[1,2,3,4,5,6]5.队列方法队列方法讲究先进先出,在列表的末尾添加项,开头删除项。push()方法上面讲到了末尾添加一项或多项shift()方法开头删除var arr = [1,2,3,4];arr.push(5,6,7); // 向末尾添加5,6,7console.log(arr); //输出[1,2,3,4,5,6,7]arr.shift(); // 删除第一项console.log(arr); //输出[2,3,4,5,6,7]pop()方法删除末尾最后一项unshift()方法向开头添加一项或多项元素var arr = [1,2,3,4];arr.unshift(5,6,7); // 向开头添加5,6,7console.log(arr); //输出[1,2,3,4,5,6,7]arr.pop(); // 删除最后一项console.log(arr); //输出[5,6,7,1,2,3]6.重排序方法reverse()反转数组改变顺序var arr = [1,2,3,4,5];arr.reverse();console.log(arr); //输出[5,4,3,2,1]sort()方法比较的是字符串,一个一个的字符串进行比较,数值小的在前面var arr = [1,6,13,40,15];arr.sort();console.log(arr); //输出[1, 13, 15, 40, 6]7.操作方法concat()方法基于当前数组创建一个副本,向后面添加新的对象,然后返回新的数组concat()方法不会影响原数组。var arr = [1,6,13,40,15];var arr2 = arr.concat(2,2,2);console.log(arr); //输出[1, 6, 13, 40, 15]console.log(arr2); //输出[1, 6, 13, 40, 15,2,2,2]slice()方法可以接受一个或者两个参数,第一个参数表示起始位置,第二个表示结束位置,slice()方法返回起始位置到结束位置的数组slice()方法不会影响原数组。var arr = [1,6,13,40,15];var arr2 = arr.slice(2);var arr3 = arr.slice(1,3);console.log(arr); //输出[1,6,13,40,15]console.log(arr2); //输出[13, 40, 15]console.log(arr3); //输出[6, 13]splice()方法,这个方法在我认为算是数组里面最强大的方法啦!他可以传入三个参数,第一个参数起始位置,第二个参数要删除的项,第三个参数添加或者替换的对象。可以利用splice()方法对数组进行删除、添加、替换等操作splice()方法会影响原数组。var arr = [1,2,3,4,5];// 删除操作var arr2 = arr.splice(1,3); // 从下标为1开始删除3项console.log(arr); //输出[1,5] 原数组只剩下第一位和最后一位console.log(arr2); //输出[2,3,4] 被删除的对象生成数组var arr = [1,2,3,4,5]; // 插入var arr2 = arr.splice(1,0,2,2);// 从下标1开始插入2,2两个对象console.log(arr); //输出[1,2,2,2,3,4,5]console.log(arr2); //输出[] 因为没有删除对象所以返回空数组var arr = [1,2,3,4,5]; // 替换var arr2 = arr.splice(1,2,2,2); // 从下标1开始先删除两项然后替换为2,2console.log(arr); //输出[1,2,2,4,5]console.log(arr2); //输出[2,3] 被删除的元素8.位置方法查找数组位置方法有两种,indexOf()和lastIndexOf()方法都可以接收两个参数,要查找的项和(可选)查找起始项位置的索引。indexOf()是从前往后找,lastIndexOf()是从后往前找。var arr = [1,2,3,4,5];console.log(arr.indexOf(2)); //1 返回下标的位置console.log(arr.indexOf(2,3)); //-1 从下标3位置开始往后找,没有找到2返回-1console.log(arr.lastIndexOf(2,3)); //1 从下标3位置开始往前找9.迭代方法ECMAScript5为数组提供了五种迭代方法,每种方法都有两个参数,要在每一项上运行的函数和(可选)运行该函数的作用域对象——影响this的值。传入这些方法的函数会接受三个形参(item,index,array):数组项的值、数组对象在数组中的位置、数组对象本身。every()给定函数后,如果该函数对每一项都返回true,则返回true。var arr = [1,2,3,4,5];var arr2 = arr.every(function(item,index,array){  return item > 3;});console.log(arr2); // false 必需每项都满足条件才可以filter()给定函数后,返回true的项组成的数组。var arr = [1,2,3,4,5];var arr2 = arr.filter(function(item,index,array){ return item > 3;});console.log(arr2); // 4,5 只有4,5满足条件forEach()给定函数后,这个方法没有返回值。本质上和for循环一样。var arr = [1,2,3,4,5];arr.forEach(function(item,index,array){  console.log(item); // 1,2,3,4,5});console.log(arr); // [1,2,3,4,5] 不会改变原数组也没有返回值map()给定函数后,返回每次函数调用的结果组成的数组。var arr = [1,2,3,4,5];var arr2 = arr.map(function(item,index,array){  return item * 2;});console.log(arr2); // [2,4,6,8,10]some()给定函数后,如果该函数任一项返回true,则返回true。var arr = [1,2,3,4,5];var arr2 = arr.some(function(item,index,array){  return item > 4;});console.log(arr2); // true 5>4返回true10.缩小方法ECMAScript5新增了2两个缩小数组的方法,reduce()和reduceRight()。这两个方法都会迭代数组所有的项,reduce()是从第一项到最后一项迭代,reduceRight()则是相反的。这两个方法可以接收两个参数,要执行的函数和(可选)做为缩小基础的初始值。执行函数可以传入四个参数(prev,cur,index,array):前一个值、当前值、数组对象索引、数组对象本身。var arr = [1,2,3,4,5];var arr2 = arr.reduce(function(prev,cur,index,array){  return prev * cur;});console.log(arr2); // 120 前一项乘后一项...

Web前端基础:带您深入了解CSS中的颜色属性

现在出来的前端工程师,基本都是在用框架干活,忽视了原有的基础知识,这是不可取的,我只能说,被框架“祸害”了。所以基础知识不能丢啊,今天来谈谈很原始的话题,CSS中的颜色属性。颜色在使网页是否可用方面起着至关重要的作用。在CSS中,我们可以使用color和background属性控制元素的前景色和背景色。几年前,当我学习CSS时,没有一种直接的指南或方法来学习如何在CSS中使用颜色。我决定写一些颜色以及如何在CSS中使用它们。在本文中,我将解释颜色类型,特定关键字以及何时使用哪种颜色以及用例和示例。免责声明:这不是有关颜色理论的文章。颜色属性根据MDN:colorCSS属性设置元素的文本和文本装饰的前景色值,并设置该currentcolor值。该属性设置元素的前景色值。它允许的值是:named-color,hex-color,rgb(),hsl,inherit,initial,unset,和currentColor。我将详细介绍每种值类型。命名的颜色值默认情况下,CSS已命名颜色,可以通过键入颜色名称来使用颜色。这里有些例子:.heading {    color: brown;    color: green;    color: black;}如果您有兴趣,请检查颜色的完整列表。值得一提的是,并非所有浏览器都支持某些颜色名称。通常,我不喜欢使用命名颜色,因为记住每种颜色的含义对我来说并不容易。例如,有时我会看到人们使用white或black作为颜色值。当然,这不是一件坏事,我无法判断。但总的来说,我不建议使用命名颜色。我将使用命名颜色的唯一情况如下:*, *:before, *:after {    outline: solid 1px red;}我使用red颜色来快速显示页面上所有元素的轮廓。这种red颜色很流行,我什至看到它的模因和有趣的东西。十六进制颜色值十六进制颜色由六个字符组成#ffffff,例如代表白色。我想确保您知道一个提示,这是十六进制值的简写形式。它的工作方式是,如果一对颜色值相同,则可以删除第二个颜色值。每对代表一种颜色模式,分别为红色,绿色和蓝色。考虑以下:body { color: #222222; /* Will become #222 */body { color: #000000; /* Will become #000 */#222222是等于#222,则十六进制值可以被表示22,22,22,我们从每一个,使得导致了所述第一值#222。让我们看一下解释其工作原理的视觉效果。RGB/RGBA颜色红色绿色蓝色(RGB)是代表三种颜色通道(红色,绿色,蓝色)的颜色模型。这三个值中的每个值可以是介于0和之间的数字,255或者是0到之间的百分比100%。如果三个值均为0,则结果颜色将为黑色。但是,如果所有值均为255,则颜色将为白色。百分比值同样适用。.element {    /* White */    color: rgb(255, 255, 255);    color: rgb(100%, 100%, 100%);    /* Black */    background-color: rgb(0, 0, 0);    background-color: rgb(0%, 0%, 0%);}好处rgb是,有一个Alpha通道可以控制颜色的透明度。这对于具有色彩可能非常有用。我将在本文中介绍更多有用的用例。.element {    /* A black background color with 50% transparency */    background-color: rgba(0, 0, 0, 0.5);}如果alpha的值为零,则颜色或背景将是透明的并且不可见。HSL颜色我很少注意到这种颜色模型的用法,但是最近它开始引起设计师和开发人员的更多关注。HSL代表:色相,饱和度,亮度。在色轮中,每种颜色都有一定的程度,即色调。然后,我们仍然需要设置饱和度和亮度。让我引导您完成计算HSL颜色值的过程。假设我们选择了以下颜色。对于饱和度和亮度,这里是他们的想象,我从中学到的一个很好的视觉方式本文章。一旦选择了色相度,我们就可以想象出饱和度和亮度,如下图所示。饱和度从灰色开始,以色相颜色结束。亮度从黑色开始变为色调颜色,最后变为白色。这种灵活性非常有用,您可以选择一种色调颜色,然后根据需要调整饱和度和亮度。这可以帮助您创建动态且易于使用的调色板。该currentColor关键字在currentColor持有一个元素的价值color属性。它可用于默认情况下不继承颜色的元素。以下是一些默认为currentColor的属性:边框颜色文字装饰颜色轮廓色盒子阴影感觉currentColor类似于使用CSS变量,但是在支持CSS变量之前就已经存在。考虑以下:.element {    color: #222;    border: 2px solid;}你能猜出边框的颜色吗?与color属性相同。这是因为border-colorhas currentColor作为默认变量。您可以这样想象:.element {    color: #222;    border: 2px solid currentColor;}关于的一个有趣的事情currentColor是,它可以在父元素级别或子元素级别使用。<h1>    This is a title    <span>I'm a child element</span></h1>在上面的示例中,我们可以currentColor为<h1>或<span>元素使用。见下文:h1 {    color: #222;    background-color: currentColor;}使用DevTools切换色彩模式在CSS中尝试颜色时,有用的一个小技巧是在ChromeDevTools中使用切换器。选择具有十六进制颜色值的任何元素,然后检查它。然后,单击显示当前使用颜色的小方块。上面仅适用于十六进制颜色,以及直接使用CSS变量的属性,如下所示。.btn {    background-color: var(--brand-primary);}但是,结合使用HSL颜色和CSS变量的缺点是浏览器之间的行为仍然不一致。例如,上面的示例在Firefox中不会显示彩色正方形。请参见下图进行比较:Safari中的蓝色小方块显示使用的CSS属性的计算值。有总比没有好。您可能想知道为什么在属性旁边必须有一个正方形来代表计算出的颜色?没有正方形,我们将无法打开DevTools调色板,这将帮助我们检查是否可以访问颜色。这并非总是如此。例如,按钮元素具有color:#fff和设置为的背景色hsl。我们仍然可以从该color属性打开DevTools调色板,并查看对比度。但是,对于使用hsla带有CSS变量的值的元素,这是不可能的。空格分隔的功能颜色标记使用rgb和的常见方法hsl是用逗号分隔它们的值。在不使用逗号的浏览器中,一种新方法已经具有强大的支持。/* Before */.element {  background-color: rgb(0, 0, 0);  background-color: rgba(0, 0, 0, 1);}/* After */div {  background-color: rgb(0 0 0);  background-color: rgb(0 0 0 / 0.5);  background-color: rgb(0 0 0 / 50%);}请记住,以斜杠分隔的不透明度是可选的,并且仅当要调整Alpha或颜色透明度时才应使用它。最好使用这种新方法,并建议将其用作新的CSS颜色函数(如lab(),)lch(),并且color()仅适用于以空格分隔的技术。习惯它们是一件好事,并且您可以轻松地为不支持的浏览器进行后备。免责声明:尽管我建议使用以空格分隔的语法,但是由于它们是新的,因此我不在本文中使用它们,并且我也不想引入任何不必要的混淆或误解。全局值(继承,初始,未设置)我们可以使用全局值来让元素从其父元素继承颜色,或者使用initial或unset关键字重置元素的颜色。让我们以以下示例了解这些关键字。我们有一个带有标题,描述和链接的英雄版块。<section class="hero">    <h2>Page title</h2>    <p>Some description content</p>    <a href="#">View all</a></section>.hero { color: #222; }随着color:#222加入到父元素,你能指望什么子元素的颜色?在<h2>与<p>将继承颜色,而<a>默认情况下不会继承。有趣,不是吗?默认链接颜色为::link { color: #0000ee; }如果需要,可以通过添加链接来强制链接继承color:inherit。该unset关键字根据MDN:如果属性自然地从其父级继承,则unsetCSS关键字会将属性重置为其继承的值,否则将其重置为初始值。对于上面说明的相同示例,我们可以使用以下命令让链接继承颜色:.hero a { color: unset; }它将颜色重置为该.hero元素的默认继承值,这就是我们想要的。用例和范例我认为理论和概念就足够了。让我们跳入一些可以在日常工作中使用的用例。透明rgba()色我认为适合rgba()CSS的一个常见用例是以下设计。请注意,每个圆圈的边框都比其背景要暗。我们如何动态地做到这一点?好吧,通过使用具有透明色值的边框rgba(),这是可能的。.item {    border: 10px solid rgba(0, 0, 0, 0.2);    /* A black border with 20% alpha value */}可以将相同的概念用于具有背景的元素,而其子元素具有更暗的变化。考虑下图:使用hsl颜色我对hsla颜色的喜欢是,我们需要选择一个色相值(度),然后我们可以使用饱和度和亮度来使颜色变亮或变暗。这对于两个用例可能很有用,例如,按钮的悬停效果可能具有较低的亮度值,使其变得更暗。.btn {  /* Hue: 97, Saturation: 50%, Brightness: 41% */    background: hsl(97, 50%, 41%);}.btn:hover {    background: hsl(97, 50%, 36%);}我们可以通过组合hsl颜色和CSS变量来创建可以轻松更改的调色板,从而进一步实现这一目标。:root {  --primary-h: 97;  --brand-primary: hsl(var(--primary-h), 50%, 41%);  --brand-primary-darker: hsl(var(--primary-h), 50%, 36%);  --brand-grey-1: hsl(0, 0%, 0%);  --brand-grey-2: hsl(0, 0%, 10%);  --brand-grey-3: hsl(0, 0%, 20%);  --brand-grey-4: hsl(0, 0%, 30%);  --brand-grey-5: hsl(0, 0%, 40%);  --brand-grey-6: hsl(0, 0%, 50%);}通过定义主要色相颜色,我们现在可以使用它来覆盖亮度或饱和度以使元素更亮或更暗。我特别喜欢使用灰色。在记住正确的灰色十六进制值时,我总是会遇到困难。使用hsl,这更加容易和直接。请注意,我是如何通过一种色调并仅通过更改亮度值来生成托盘的。现在使用灰色难道不是容易一百万倍吗?我真的很喜欢它,并将考虑在即将到来的客户项目中使用它。在处理大型项目时,我将对CSS变量使用以下方法来更改明度值。请参阅以下内容::root {  --primary-h: 97;  --primary-l: 41%;  --brand-primary: hsl(var(--primary-h), 50%, var(--primary-l));  --brand-primary-darker: hsl(var(--primary-h), 50%, 36%);}我定义--primary-h了代表色相值和--primary-l基本亮度值。现在有趣的是,我可以仅通过更改--primary-l变量来调整颜色。.btn {    --primary-l: 45%;    background-color: hsl(var(--primary-h), 50%, var(--primary-l));}.footer {    --primary-l: 55%;    background-color: hsl(var(--primary-h), 50%, var(--primary-l));}.section {    --primary-l: 50%;    background-color: hsl(var(--primary-h), 50%, var(--primary-l));}将CurrentColor与SVG图标一起使用一个可爱的用例currentColor是SVG图标。假设我们旁边有一个带有文本标签的图标。图标和标签应具有相同的颜色。我们可以使用currentColorSVG fill,然后在父元素上分配颜色。考虑以下:<a href="#" class="link">    <svg fill="currentColor"></svg>    <span>Show more</span></a>  .link {    color: #3766dc;}这样,SVG将采用指定的颜色。除此之外,我们可以使用相同的技术将图标包裹在正方形中,并且该正方形具有不透明度,填充值为currentColor。这样就可以使用color来:为图标分配填充使用相同的图标颜色为正方形分配色彩参见下图。.icon {  color: hsl(var(--primary-h), 50%, var(--primary-l));}.icon-2 {  --primary-h: 26;}.icon-3 {  --primary-h: 292;}请注意,它<rect>具有不透明度。我更改的只是hue每个图标变体的值。对于设计系统,这对于使图标及其背景使用相同的颜色而没有多余的颜色,将非常有用。总结如果您认真看完了以上内容,应该会深入了解关于color的很多知识了。...

Web前端开发中,如何用Javascript获取文件后缀名

Web前端开发中,如何用Javascript获取文件后缀名呢?我们一起来了解下。在上传文件时,常常要对文件的类型即对文件的后缀名进行判断,用javascript可以很容易的做到这一点。用Javascript解析一个带绝对路径的文件名并得到后缀名的方法有很多种,这里列出一种,以供参考。对于一个带绝对路径的文件名如:D:\ProgramFiles\Notepad++\Notepad++.exe首先为了避免转义反斜杠出问题,可以用正则表达式来将\或\\替换成#,如:D:#ProgramFiles#Notepad++#Notepad++.exe之后以‘#'为分隔符,将字符串分解成数组,得到如下数组:D: ProgramFiles Notepad++ Notepad++.exe取数组的最后一个即为带后缀的文件名:Notepad++.exe再以‘.'为分隔符,将这个带后缀的文件名分解成数组,得到如下数组:Notepad++ exe然后再取数组的最后一个就可以得到文件的后缀名exe了代码如下(Win7+IE9测试通过)://by MoreWindows (http://blog.csdn.net/MoreWindows)function GetExtensionFileName(pathfilename){    var reg = /(\\+)/g;    var pfn = pathfilename.replace(reg, "#");    var arrpfn = pfn.split("#");    var fn = arrpfn[arrpfn.length - 1];    var arrfn = fn.split(".");    return arrfn[arrfn.length - 1];}DEMO代码:function Test(){    var filePath="D:\\Program Files\\Notepad++\\Notepad++.exe";    alert(GetExtensionFileName(filePath));}<input type="button" value="Test" οnclick="Test()" />点击Test按钮就可以弹出内容为exe的对话框,表示GetExtensionFileName能正确解析带绝对路径的文件名并得到后缀名。估计这种方法只能在Windows平台下使用吧,在Linux执行下不知道会如何?...

Web前端开发中,如何优化前端数组处理方法?

Web前端开发中,如何优化前端数组处理方法?也许你已经遇到过讨论,我们是否应该使用Array方法(filter,map,reduce等)或循环(for,for...of,等)遍历集合。关于数组的一些方法,我们分享过文章:JS数组遍历之for循环方法应用解析分享几种原生JS数组遍历的方法和应用jquery数组封装使用方法分享(jquery数组遍历)jquery进行数组遍历如何跳出当前的each循环大家有兴趣的,可以去一一了解下。一段好的代码,会让你的程序性能更好且易理解,运行速度加快很多,相信大家都会知道这一点。性能和可读性是选择一个而不是另一个的两个常见论点。让我们更好地看一下循环和Array方法之间的差异并进行比较,以便我们自己决定。性能在ECMAScript5之前,我们所拥有的只是循环和jQuery $.each。哪种迭代最快的方法经常引起争议。从那时起,情况有了很大的改善。从那时起,我们智能手机中的CPU的性能就超过了计算机,我们现在可以在客户端做更多很棒的工作,而不会崩溃整个浏览器。尽管如此,Array仍然提高了方法的性能。TIPs:如果要遍历大量项目,则可能要避免使用Array方法。在大多数情况下,它不应有太大的区别。对于大多数项目,很少要遍历一百多个项目的列表。您通常可以使用您认为更易读的内容。只要确保始终检查低端设备的性能即可。可读性我们可以同意可读性很重要。这也是主观的。例如,让我们使用带有隐式return((a,b)=>a+b)的箭头函数。初学者可能会将其与逻辑表达式混淆。大于或等于运算符(>=)看起来确实相似。也许他们可能没有意识到函数返回值。毕竟,返回是隐式的。训练有素的眼睛会立即注意到箭头的功能。对于所有其他语法也可以这样说。由我们决定培训初学者开发人员或不使用功能,以便任何技能水平的开发人员都可以访问代码库。话虽如此,让我们比较一些循环和Array方法的可读性。Array.prototype.forEach而且for...of都易于阅读。posts.forEach((post)=>console.log(post));//orfor(constpostofposts){console.log(post);}等效的循环Array.prototype.filter有点显式。constdrafts=posts.filter((post)=>post.isDraft);//orconstdrafts=[];for(constpostofposts){if(post.isDraft){drafts.push(post);}}也是一样Array.prototype.map。constpublishDates=post.map((post)=>post.date);//orconstpublishDates=newArray(posts.length);for(leti=0;i<posts.length;i++){publishDates[i]=posts[i].date;}Array.prototype.reduce确实很棒,但是可能要花几秒钟来了解正在发生的事情。即使对于类似求和的运算,您可能也需要一秒钟才能理解。当reduce回调包含更多逻辑时,可读性迅速变差。consttotalWordCount=posts.reduce((result,post)=>result+post.wordCount,0);//orlettotalWordCount=0;for(constpostofposts){totalWordCount+=post.wordCount;}对于其他方法,例如Array.prototype.every,,Array.prototype.find和Array.prototype.some,循环并不可怕,并且在具有早期返回功能的函数中效果更好。它们绝对比Array方法更明确。constisEverythingPublished=posts.every((post)=>post.isDraft);//orletisEverythingPublished=true;for(constpostofposts){if(post.isDraft){isEverythingPublished=false;break;}}//orinafunctionwithearlyreturnfunctiongetIsEverythingPublished(posts){for(constpostofposts){if(post.isDraft){returnfalse;}}returntrue;}constisEverythingPublished=getIsEverythingPublished(posts);如果您喜欢明确的话,循环可能会吸引您。对我来说,它们有点冗长,我个人更喜欢Array除之外的方法Array.prototype.reduce。但同样,可读性是主观的。这是私事。特点与用途尽管看起来循环和Array方法是做同一件事的两种方式,但是它们在显着方式上是不同的。首先,Array方法是同步的。如果将异步函数传递给Array方法,它将不会等待异步函数完成。循环不依赖于回调,因此,如果您已经处于异步上下文中,则可以await在循环块中或使用forawait...of循环。考虑下面的示例。这是一劳永逸的操作,传递给异步函数的Array.prototype.forEach调用被调用,但forEach不等待该函数完成。consturls=['https://www.jiangweishan.com','https://www.web176.com',];urls.forEach(async()=>{constresponse=awaitfetch(url);if(!response.ok){console.warn(`Unabletofetch${url}`);}});在下面的示例中,我们将异步函数传递给Array.prototype.map调用。因为一个异步函数总是返回Promise时,返回值map调用的列表Promise。我们可传递给Promise.all,Promise.allSettled或Promise.race。这样,我们可以并行运行各种异步任务并等待结果。asyncfunctionassertPageIsOk(url){constresponse=awaitfetch(url);if(!response.ok){thrownewError(`"GET${url}"respondedwith${response.statusCode}`);}returntrue;}consturls=['https://timseverien.com','https://timseverien.com/posts',];try{awaitPromise.all(urls.map(assertPageIsOk));}catch(error){console.error(error);}让我们使前面的示例稍微复杂一点。让我们依次调用每个URL,而不是同时触发所有请求(可能导致拒绝服务)。如果其中之一返回非2xx状态代码,我们可以停止序列。functionassertPagesAreOk(urls){returnurls.reduce(async(chain,url)=>{awaitchain;returnassertPageIsOk(url);},Promise.resolve());}assertPagesAreOk(urls).then(()=>...).catch(()=>...);这是上面的循环等效项:asyncfunctionassertPagesAreOk(urls){for(consturlofurls){awaitassertPageIsOk(url);}returntrue;}assertPagesAreOk(urls).then(()=>...).catch(()=>...);在以上所有示例中,我发现循环均等或更具可读性。循环和Array方法之间的第二个重要区别是数组不能无限大。通常,我们认为无限循环是一件坏事,但它们可能非常有用。想象一下,我们正在开发一个现代散点图库,该库中我们想在画布上绘制大量点。由于数组大小有限,因此我们可以通过允许用户传递生成器函数来利用迭代器。importcreateScatterPlotfrom'scatman';createScatterPlot({data:*function(){while(true){yield{x:Math.random(),y:Math.random(),};}},});生成器函数返回生成器对象,它是迭代器的超集。它们可以转换为数组(通过[...iterator]或Array.from(iterator)),也可以循环使用。如前所述,数组的最大大小是一个限制。另一方面,在循环中,当循环需要下一个值时,会将各个值从迭代器中拉出。因为我们在示例中生成了无限点,所以将其转换为数组将冻结浏览器。for(const{x,y}ofdata){draw(x,y);awaitwaitForNextFrame();}总结我们已经知道循环速度更快,但是Array当方法内部处理某些逻辑(例如创建新数组或滤除特定值)时,方法可以提高可读性。我们还看到,在处理异步任务时,循环更具可读性,并了解到循环对于迭代未知数量的项目很有用,这在迭代器中很常见。在大多数情况下,我们将使用它们中的任何一个来遍历少于一千个项目的数组,在这种情况下,我们应该强烈倾向于更具可读性的选项。我觉得Array除以外的方法更具可读性Array.prototype.reduce。我经常看到前者在使用这种方法时遇到了麻烦,而没有完全了解它的行为。无论如何,没有人比您更了解您,您的团队或您的项目。也许您是在一个全栈的团队中工作的,他们对循环比对Array方法更熟悉。也许这些Array方法更符合您的函数式编程原理。您(和您的团队)将不得不弄清楚这一点。...

Web前端开发:构造完函数,如何去调用呢?

我们在前端开发过程中,构造完函数,如何去调用呢?分享下几种方法:第一种:函数立即调用执行模式。这里面的this指向window。 function add(a,b){        console.log(this);        return a+b;}add();//this === window //true第二种:通过构造函数创建对象,然后调用自己的方法;这里的this指向对象本身;也可说是函数的调用者。<script>    function fun(){        this.show=function(){            console.log(this);        }    }    var f=new fun();    f.show();//f对象;</script>第三种:通过构造器调用函数:this指向构造出来的对象;<script>    function Cat(){        console.log(this);    }    Cat.prototype.show=function(){        console.log(this);    }    var t=new Cat();//cat{};//通过构造函数创建的对象,相当于直接调用函数,没有返回值的情况下,得到的是cat本身;    t.show();//cat{}对象;    console.log(t==this);//false;    Cat.prototype.show();//show{};    Cat();//直接调用window</script>...

聊聊Web前端开发中关于JavaScript closest()的一些应用

Web前端开发中关于JavaScript里closest()的一些基础知识。您是否曾经遇到过在JavaScript中找到DOM节点的父节点的问题,但是不确定是否要遍历多少个层次才能到达它?让我们来看一下这个HTML:<div data-id="123">  <button>Click me</button></div>那很简单,对吧?假设您要data-id在用户单击按钮后获取的值:var button = document.querySelector("button");button.addEventListener("click", (evt) => {  console.log(evt.target.parentNode.dataset.id);  // prints "123"});在这种情况下,Node.parentNodeAPI就足够了。它的作用是返回给定元素的父节点。在上面的示例中,evt.target单击了按钮;它的父节点是具有data属性的div。但是,如果HTML结构嵌套得更深呢?根据其内容,它甚至可以是动态的。<div data-id="123">  <article>    <header>      <h1>Some title</h1>      <button>Click me</button>    </header>     <!-- ... -->  </article></div>通过添加更多HTML元素,我们的工作变得更加困难。当然,我们可以做类似的事情element.parentNode.parentNode.parentNode.dataset.id,但是来吧……那不是优雅,可重用或可扩展的。旧方法:使用while-loop一种解决方案是利用while循环,直到找到父节点为止。function getParentNode(el, tagName) {  while (el && el.parentNode) {    el = el.parentNode;        if (el && el.tagName == tagName.toUpperCase()) {      return el;    }  }    return null;}从上面再次使用相同的HTML示例,它看起来像这样:var button = document.querySelector("button");console.log(getParentNode(button, 'div').dataset.id);// prints "123"该解决方案远非完美。想象一下,如果您要使用ID或类或任何其他类型的选择器,而不是标签名。至少它允许在父级和我们的源之间有可变数量的子节点。还有jQuery过去,如果您不想处理上面为每个应用程序编写的上述函数(让我们成为现实,谁想要的?),那么像jQuery这样的库就派上用场了(它仍然可以)。它提供了.closest()一种确切的方法:$("button").closest("[data-id='123']")新方法:使用Element.closest()即使jQuery仍然是一种有效的方法(嘿,我们当中有些人对此很执着),但是仅将这一种方法添加到项目中就太过头了,特别是如果您可以使用本机JavaScript拥有同样的方法。这就是起作用的地方Element.closest:var button = document.querySelector("button");console.log(button.closest("div"));// prints the HTMLDivElement好了!这很容易,而且没有任何库或额外的代码。Element.closest()允许我们遍历DOM,直到获得与给定选择器匹配的元素。令人敬畏的是,我们可以传递也要给Element.querySelector或的任何选择器Element.querySelectorAll。它可以是ID,类,数据属性,标签等。element.closest("#my-id"); // yepelement.closest(".some-class"); // yepelement.closest("[data-id]:not(article)") // hell yeah如果Element.closest根据给定的选择器找到父节点,则它会与返回相同的方式  document.querySelector。否则,如果找不到父项,它将返回null,从而易于使用if条件:var button = document.querySelector("button");console.log(button.closest(".i-am-in-the-dom"));// prints HTMLElementconsole.log(button.closest(".i-am-not-here"));// prints nullif (button.closest(".i-am-in-the-dom")) {  console.log("Hello there!");} else {  console.log(":(");}准备好一些真实的例子了吗?我们走吧!用例1:下拉列表<!DOCTYPE html>    <html>    <head>    <meta charset="UTF-8">    <link rel="apple-touch-icon" type="image/png" href="https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" />    <meta name="apple-mobile-web-app-title" content="CodePen">    <link rel="shortcut icon" type="image/x-icon" href="https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />    <link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111" />    <meta charset="utf-8">    <meta name='viewport' content='width=device-width, initial-scale=1'>    <title>CodePen - Dropdown Example with `Element.closest`</title>    <link rel="stylesheet" media="screen" href="https://static.codepen.io/assets/fullpage/fullpage-4de243a40619a967c0bf13b95e1ac6f8de89d943b7fc8710de33f681fe287604.css" />    <link href="https://fonts.googleapis.com/css?family=Lato:300,400,400italic,700,700italic,900,900italic" rel="stylesheet" />    <link rel="apple-touch-icon" type="image/png" href="https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" />    <meta name="apple-mobile-web-app-title" content="CodePen">    <link rel="shortcut icon" type="image/x-icon" href="https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />    <link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111" />    <title>CodePen - Dropdown Example with `Element.closest`</title>    <script>    if (document.location.search.match(/type=embed/gi)) {    window.parent.postMessage("resize", "*");    }    </script>    <style>    html { font-size: 15px; }    html, body { margin: 0; padding: 0; min-height: 100%; }    body { height:100%; display: flex; flex-direction: column; }    .referer-warning {    background: black;    box-shadow: 0 2px 5px rgba(0,0,0, 0.5);    padding: 0.75em;    color: white;    text-align: center;    font-family: 'Lato', 'Lucida Grande', 'Lucida Sans Unicode', Tahoma, Sans-Serif;    line-height: 1.2;    font-size: 1rem;    position: relative;    z-index: 2;    }    .referer-warning h1 { font-size: 1.2rem; margin: 0; }    .referer-warning a { color: #56bcf9; } /* $linkColorOnBlack */    </style>    </head>    <body class="">    <div id="result-iframe-wrap" role="main">    <iframe id="result" srcdoc="    <!DOCTYPE html>    <html lang=&quot;en&quot; >    <head>     <meta charset=&quot;UTF-8&quot;>    <link rel=&quot;apple-touch-icon&quot; type=&quot;image/png&quot; href=&quot;https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png&quot; />    <meta name=&quot;apple-mobile-web-app-title&quot; content=&quot;CodePen&quot;>    <link rel=&quot;shortcut icon&quot; type=&quot;image/x-icon&quot; href=&quot;https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico&quot; />    <link rel=&quot;mask-icon&quot; type=&quot;&quot; href=&quot;https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg&quot; color=&quot;#111&quot; />     <title>CodePen - Dropdown Example with `Element.closest`</title>     <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600&amp;amp;display=swap'>    <style>    * {     box-sizing: border-box;    }    body {     font: normal 14px/1.4 &quot;Source Sans Pro&quot;, sans-serif;     background-color: #D9E2EC;     color: #334E68;     margin: 2rem;    }    /* Navigation */    .main-navigation {     background-color: white;     padding: 1.5rem 2rem;     border-radius: .3em;     box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05);     display: flex;     align-items: center;    }    .main-navigation.is-expanded {     border-bottom-left-radius: 0;    }    .main-navigation > .entry {     font: inherit;     color: inherit;     text-decoration: unset;     border: unset;     background-color: unset;     padding: unset;     font-weight: 600;     cursor: pointer;     margin-right: 2rem;     transition: color .1s linear;     position: relative;    }    .main-navigation > .entry.is-dropdown {     padding-right: 1rem;    }    .main-navigation > .entry.is-dropdown::after {     content: &quot;&quot;;     position: absolute;     width: 0;     height: 0;     border-top: 8px solid #2CB1BC;     border-left: 5px solid transparent;     border-right: 5px solid transparent;     top: 7px;     right: 0px;    }    .main-navigation > .entry:hover, .main-navigation > .entry:focus {     color: #2CB1BC;    }    /* Dropdown */    .menu-dropdown {     background-color: white;     position: absolute;     margin: unset;     padding: 1.5rem 2rem;     list-style: none;     border-radius: 0 0 .3em .3em;     box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05);     display: flex;     flex-wrap: wrap;     max-width: 650px;     border-top: 3px solid #2CB1BC;    }    .menu-dropdown.is-hidden {     display: none;    }    .menu-dropdown > .item {     flex: 0 0 50%;    }    .menu-dropdown > .item:nth-child(odd) {     padding-right: 1rem;    }    .menu-dropdown > .item:nth-child(even) {     padding-left: 1rem;    }    .menu-dropdown > .item:first-child, .menu-dropdown > .item:nth-child(2) {     margin-bottom: 2rem;    }    .menu-dropdown > .item > .title {     margin: unset;    }    .menu-dropdown > .item > .text {     margin-top: unset;     color: #668eb4;    }    .menu-dropdown > .item > .link {     color: #2CB1BC;     font-weight: 600;     text-decoration: unset;     transition: color .1s linear;    }    .menu-dropdown > .item > .link:hover, .menu-dropdown > .item > .link:focus {     color: #334E68;    }    </style>     <script>     window.console = window.console || function(t) {};    </script>     <script>     if (document.location.search.match(/type=embed/gi)) {       window.parent.postMessage(&quot;resize&quot;, &quot;*&quot;);     }    </script>    </head>    <body translate=&quot;no&quot; >     <nav class=&quot;main-navigation&quot;>     <button type=&quot;button&quot; class=&quot;entry is-dropdown&quot; data-dropdown-trigger>Resources</button>     <button type=&quot;button&quot; class=&quot;entry is-dropdown&quot; data-dropdown-trigger>Customers</button>     <a href=&quot;#&quot; class=&quot;entry&quot;>Contact</a>    </nav>    <ul class=&quot;menu-dropdown is-hidden&quot;>     <li class=&quot;item&quot;>       <h3 class=&quot;title&quot;>Designs</h3>       <p class=&quot;text&quot;>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Soluta, amet?</p>       <a class=&quot;link&quot; href=&quot;#&quot;>More</a>     </li>     <li class=&quot;item&quot;>       <h3 class=&quot;title&quot;>Tutorials</h3>       <p class=&quot;text&quot;>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Soluta, amet?</p>       <a class=&quot;link&quot; href=&quot;#&quot;>More</a>     </li>     <li class=&quot;item&quot;>       <h3 class=&quot;title&quot;>Templates</h3>       <p class=&quot;text&quot;>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Soluta, amet?</p>       <a class=&quot;link&quot; href=&quot;#&quot;>More</a>     </li>     <li class=&quot;item&quot;>       <h3 class=&quot;title&quot;>Mockups</h3>       <p class=&quot;text&quot;>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Soluta, amet?</p>       <a class=&quot;link&quot; href=&quot;#&quot;>More</a>     </li>    </ul>       <script src=&quot;https://static.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js&quot;></script>         <script id=&quot;rendered-js&quot; >    var menu = document.querySelector(&quot;.menu-dropdown&quot;);    var navigation = document.querySelector(&quot;.main-navigation&quot;);    function handleClick(evt) {     // Only if a click on a dropdown trigger happens, either close or open it.     if (evt.target.hasAttribute(&quot;data-dropdown-trigger&quot;)) {       if (menu.classList.contains(&quot;is-hidden&quot;)) {         menu.classList.remove(&quot;is-hidden&quot;);         navigation.classList.add(&quot;is-expanded&quot;);       } else {         menu.classList.add(&quot;is-hidden&quot;);         navigation.classList.remove(&quot;is-expanded&quot;);       }       return;     }     // If a click happens somewhere outside the dropdown, close it.     if (!evt.target.closest(&quot;.menu-dropdown&quot;)) {       menu.classList.add(&quot;is-hidden&quot;);       navigation.classList.remove(&quot;is-expanded&quot;);     }    }    window.addEventListener(&quot;click&quot;, handleClick);    //# sourceURL=pen.js       </script>    </body>    </html>    " sandbox="allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups allow-presentation  allow-scripts allow-top-navigation-by-user-activation" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; vr" allowTransparency="true" allowpaymentrequest="true" allowfullscreen="true">    </iframe>    </div>    </body>    </html>我们的第一个演示是下拉菜单的基本(远非完美)实现,单击一个顶级菜单项后即可打开该下拉菜单。请注意,即使在下拉菜单中的任意位置单击或选择文本,菜单也如何保持打开状态?但是单击外部的某个位置,它将关闭。该Element.closestAPI是什么检测之外点击。下拉菜单本身是<ul>带有.menu-dropdown类的元素,因此单击菜单外的任何位置都会将其关闭。这是因为for的值evt.target.closest(".menu-dropdown")将是null因为此类没有父节点。function handleClick(evt) {  // ...    // if a click happens somewhere outside the dropdown, close it.  if (!evt.target.closest(".menu-dropdown")) {    menu.classList.add("is-hidden");    navigation.classList.remove("is-expanded");  }}在handleClick回调函数中,条件决定了要做什么:关闭下拉列表。如果单击无序列表中的其他位置,Element.closest将找到并返回它,从而使下拉列表保持打开状态。用例2:表格<!DOCTYPE html>    <html>    <head>    <meta charset="UTF-8">    <link rel="apple-touch-icon" type="image/png" href="https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" />    <meta name="apple-mobile-web-app-title" content="CodePen">    <link rel="shortcut icon" type="image/x-icon" href="https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />    <link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111" />    <meta charset="utf-8">    <meta name='viewport' content='width=device-width, initial-scale=1'>    <title>CodePen - Table Example with `Element.closest`</title>    <link rel="stylesheet" media="screen" href="https://static.codepen.io/assets/fullpage/fullpage-4de243a40619a967c0bf13b95e1ac6f8de89d943b7fc8710de33f681fe287604.css" />    <link href="https://fonts.googleapis.com/css?family=Lato:300,400,400italic,700,700italic,900,900italic" rel="stylesheet" />    <link rel="apple-touch-icon" type="image/png" href="https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" />    <meta name="apple-mobile-web-app-title" content="CodePen">    <link rel="shortcut icon" type="image/x-icon" href="https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />    <link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111" />    <title>CodePen - Table Example with `Element.closest`</title>    <script>    if (document.location.search.match(/type=embed/gi)) {    window.parent.postMessage("resize", "*");    }    </script>    <style>    html { font-size: 15px; }    html, body { margin: 0; padding: 0; min-height: 100%; }    body { height:100%; display: flex; flex-direction: column; }    .referer-warning {    background: black;    box-shadow: 0 2px 5px rgba(0,0,0, 0.5);    padding: 0.75em;    color: white;    text-align: center;    font-family: 'Lato', 'Lucida Grande', 'Lucida Sans Unicode', Tahoma, Sans-Serif;    line-height: 1.2;    font-size: 1rem;    position: relative;    z-index: 2;    }    .referer-warning h1 { font-size: 1.2rem; margin: 0; }    .referer-warning a { color: #56bcf9; } /* $linkColorOnBlack */    </style>    </head>    <body class="">    <div id="result-iframe-wrap" role="main">    <iframe id="result" srcdoc="    <!DOCTYPE html>    <html lang=&quot;en&quot; >    <head>     <meta charset=&quot;UTF-8&quot;>    <link rel=&quot;apple-touch-icon&quot; type=&quot;image/png&quot; href=&quot;https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png&quot; />    <meta name=&quot;apple-mobile-web-app-title&quot; content=&quot;CodePen&quot;>    <link rel=&quot;shortcut icon&quot; type=&quot;image/x-icon&quot; href=&quot;https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico&quot; />    <link rel=&quot;mask-icon&quot; type=&quot;&quot; href=&quot;https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg&quot; color=&quot;#111&quot; />     <title>CodePen - Table Example with `Element.closest`</title>     <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Open+Sans:400,700&amp;amp;display=swap'>    <style>    body {     margin: unset;     font: normal 14px/1.5 &quot;Open Sans&quot;, sans-serif;     color: #444;     background-color: lightblue;     padding: 1rem;    }    input[type=&quot;checkbox&quot;] {     margin: unset;    }    table {     max-width: 70vw;     margin: auto;     border-collapse: collapse;     box-shadow: 0 10px 30px rgba(0, 0, 0, .1);    }    th {     text-align: left;     padding: 10px;     background-color: rgba(0, 0, 0, .15);    }    td {     background-color: white;     padding: 10px;    }    tr:not(:last-of-type) td {     border-bottom: 1px solid #ddd;    }    </style>     <script>     window.console = window.console || function(t) {};    </script>     <script>     if (document.location.search.match(/type=embed/gi)) {       window.parent.postMessage(&quot;resize&quot;, &quot;*&quot;);     }    </script>    </head>    <body translate=&quot;no&quot; >     <table>     <thead>       <tr>         <th></th>         <th>Name</th>         <th>Email</th>         <th></th>       </tr>     </thead>     <tbody>       <tr data-userid=&quot;1&quot;>         <td>           <input type=&quot;checkbox&quot; data-action=&quot;select&quot;>         </td>         <td>John Doe</td>         <td>john.doe@gmail.com</td>         <td>           <button type=&quot;button&quot; data-action=&quot;edit&quot;>Edit</button>           <button type=&quot;button&quot; data-action=&quot;delete&quot;>Delete</button>         </td>       </tr>       <tr data-userid=&quot;2&quot;>         <td>           <input type=&quot;checkbox&quot; data-action=&quot;select&quot;>         </td>         <td>Jane Smith</td>         <td>jane@smith.com</td>         <td>           <button type=&quot;button&quot; data-action=&quot;edit&quot;>Edit</button>           <button type=&quot;button&quot; data-action=&quot;delete&quot;>Delete</button>         </td>       </tr>       <tr data-userid=&quot;3&quot;>         <td>           <input type=&quot;checkbox&quot; data-action=&quot;select&quot;>         </td>         <td>George Westminster</td>         <td>g.westminster@googlemail.com</td>         <td>           <button type=&quot;button&quot; data-action=&quot;edit&quot;>Edit</button>           <button type=&quot;button&quot; data-action=&quot;delete&quot;>Delete</button>         </td>       </tr>       <tr data-userid=&quot;4&quot;>         <td>           <input type=&quot;checkbox&quot; data-action=&quot;select&quot;>         </td>         <td>Will Johnson</td>         <td>will.johnson@yahoo.com</td>         <td>           <button type=&quot;button&quot; data-action=&quot;edit&quot;>Edit</button>           <button type=&quot;button&quot; data-action=&quot;delete&quot;>Delete</button>         </td>       </tr>     </tbody>    </table>       <script src=&quot;https://static.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js&quot;></script>         <script id=&quot;rendered-js&quot; >    (function () {     &quot;use strict&quot;;     function getUserId(target) {       return target.closest(&quot;[data-userid]&quot;).dataset.userid;     }     function handleClick(evt) {       var { action } = evt.target.dataset;       if (action) {         let userId = getUserId(evt.target);         if (action == &quot;edit&quot;) {           alert(`Edit user with ID of ${userId}`);         } else if (action == &quot;delete&quot;) {           alert(`Delete user with ID of ${userId}`);         } else if (action == &quot;select&quot;) {           alert(`Selected user with ID of ${userId}`);         }       }     }     window.addEventListener(&quot;click&quot;, handleClick);    })();    //# sourceURL=pen.js       </script>    </body>    </html>    " sandbox="allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups allow-presentation  allow-scripts allow-top-navigation-by-user-activation" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; vr" allowTransparency="true" allowpaymentrequest="true" allowfullscreen="true">    </iframe>    </div>    </body>    </html>第二个示例呈现一个显示用户信息的表,比方说它是仪表板中的组件。每个用户都有一个ID,但是我们没有显示它,而是将其另存为每个<tr>元素的数据属性。<table>  <!-- ... -->  <tr data-userid="1">    <td>      <input type="checkbox" data-action="select">    </td>    <td>John Doe</td>    <td>john.doe@gmail.com</td>    <td>      <button type="button" data-action="edit">Edit</button>      <button type="button" data-action="delete">Delete</button>    </td>  </tr></table>最后一列包含两个按钮,用于编辑和删除表中的用户。第一个按钮的data-action属性为edit,第二个按钮的属性为delete。当我们单击它们中的任何一个时,我们都想触发一些操作(例如向服务器发送请求),但是为此,需要用户ID。单击事件侦听器附加到全局窗口对象,因此,每当用户单击页面上的某个位置时,都会调用回调函数handleClick。function handleClick(evt) {  var { action } = evt.target.dataset;    if (action) {    // `action` only exists on buttons and checkboxes in the table.    let userId = getUserId(evt.target);        if (action == "edit") {      alert(`Edit user with ID of ${userId}`);    } else if (action == "delete") {      alert(`Delete user with ID of ${userId}`);    } else if (action == "select") {      alert(`Selected user with ID of ${userId}`);    }  }}如果单击发生在这些按钮之一以外的其他位置,则不data-action存在属性,因此什么也没有发生。但是,单击任一按钮时,将确定操作(顺便说一句,称为事件委托),并且下一步,将通过调用来检索用户IDgetUserId:function getUserId(target) {  // `target` is always a button or checkbox.  return target.closest("[data-userid]").dataset.userid;}该函数期望将DOM节点作为唯一参数,并在调用时用于Element.closest查找包含按下按钮的表行。然后data-userid,它返回该值,该值现在可用于将请求发送到服务器。用例3:React中的表让我们继续使用表格示例,看看如何在React项目中处理它。这是返回表的组件的代码:function TableView({ users }) {  function handleClick(evt) {    var userId = evt.currentTarget    .closest("[data-userid]")    .getAttribute("data-userid");    // do something with `userId`  }  return (    <table>      {users.map((user) => (        <tr key={user.id} data-userid={user.id}>          <td>{user.name}</td>          <td>{user.email}</td>          <td>            <button onClick={handleClick}>Edit</button>          </td>        </tr>      ))}    </table>  );}我发现这种用例经常出现-映射一组数据并将其显示在列表或表中,然后允许用户对其进行处理是相当普遍的。许多人使用嵌入式箭头功能,如下所示:<button onClick={() => handleClick(user.id)}>Edit</button>虽然这也是解决问题的有效方法,但我更喜欢使用该data-userid技术。内联箭头功能的缺点之一是,每次React重新渲染列表时,它都需要再次创建回调函数,从而在处理大量数据时可能导致性能问题。在回调函数中,我们只需提取目标(按钮)并获取<tr>包含该data-userid值的父元素来处理事件。function handleClick(evt) {  var userId = evt.target  .closest("[data-userid]")  .getAttribute("data-userid");  // do something with `userId`}用例4:模态<!DOCTYPE html>    <html>    <head>    <meta charset="UTF-8">    <link rel="apple-touch-icon" type="image/png" href="https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" />    <meta name="apple-mobile-web-app-title" content="CodePen">    <link rel="shortcut icon" type="image/x-icon" href="https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />    <link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111" />    <meta charset="utf-8">    <meta name='viewport' content='width=device-width, initial-scale=1'>    <title>CodePen - Modal Example with `Element.closest`</title>    <link rel="stylesheet" media="screen" href="https://static.codepen.io/assets/fullpage/fullpage-4de243a40619a967c0bf13b95e1ac6f8de89d943b7fc8710de33f681fe287604.css" />    <link href="https://fonts.googleapis.com/css?family=Lato:300,400,400italic,700,700italic,900,900italic" rel="stylesheet" />    <link rel="apple-touch-icon" type="image/png" href="https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" />    <meta name="apple-mobile-web-app-title" content="CodePen">    <link rel="shortcut icon" type="image/x-icon" href="https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />    <link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111" />    <title>CodePen - Modal Example with `Element.closest`</title>    <script>    if (document.location.search.match(/type=embed/gi)) {    window.parent.postMessage("resize", "*");    }    </script>    <style>    html { font-size: 15px; }    html, body { margin: 0; padding: 0; min-height: 100%; }    body { height:100%; display: flex; flex-direction: column; }    .referer-warning {    background: black;    box-shadow: 0 2px 5px rgba(0,0,0, 0.5);    padding: 0.75em;    color: white;    text-align: center;    font-family: 'Lato', 'Lucida Grande', 'Lucida Sans Unicode', Tahoma, Sans-Serif;    line-height: 1.2;    font-size: 1rem;    position: relative;    z-index: 2;    }    .referer-warning h1 { font-size: 1.2rem; margin: 0; }    .referer-warning a { color: #56bcf9; } /* $linkColorOnBlack */    </style>    </head>    <body class="">    <div id="result-iframe-wrap" role="main">    <iframe id="result" srcdoc="    <!DOCTYPE html>    <html lang=&quot;en&quot; >    <head>     <meta charset=&quot;UTF-8&quot;>    <link rel=&quot;apple-touch-icon&quot; type=&quot;image/png&quot; href=&quot;https://static.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png&quot; />    <meta name=&quot;apple-mobile-web-app-title&quot; content=&quot;CodePen&quot;>    <link rel=&quot;shortcut icon&quot; type=&quot;image/x-icon&quot; href=&quot;https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico&quot; />    <link rel=&quot;mask-icon&quot; type=&quot;&quot; href=&quot;https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg&quot; color=&quot;#111&quot; />     <title>CodePen - Modal Example with `Element.closest`</title>     <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Gelasio:400,700&amp;amp;display=swap'>    <style>    body {     margin: unset;     font: normal 16px/1.5 Gelasio, serif;     text-align: center;    }    .open-modal {     font-family: inherit;     font-size: 23px;     background-color: tomato;     border: none;     font-weight: bold;     padding: .5rem 4rem;     border-radius: .2rem;     margin-top: 3rem;     cursor: pointer;    }    .open-modal:hover {     background-color: black;     color: white;    }    .modal-outer {     position: fixed;     top: 0;     left: 0;     width: 100%;     height: 100%;     background-color: rgba(200, 200, 200, .4);     display: flex;     align-items: center;     justify-content: center;    }    .modal-outer.is-hidden {     display: none;    }    .modal-inner {     background-color: white;     position: relative;     box-shadow: 0 10px 20px -10px rgba(0, 0, 0, .1);     width: 100%;     max-width: 70vw;     max-height: 50vh;     border-radius: .5rem;     padding: 2rem;     box-sizing: border-box;     text-align: center;    }    .modal-close {     position: absolute;     top: 0;     right: 0;     font-size: 2rem;     background-color: transparent;     border: none;     margin: unset;     width: 50px;     height: 50px;     cursor: pointer;    }    .modal-close:hover,    .modal-close:focus {     color: tomato;    }    </style>     <script>     window.console = window.console || function(t) {};    </script>     <script>     if (document.location.search.match(/type=embed/gi)) {       window.parent.postMessage(&quot;resize&quot;, &quot;*&quot;);     }    </script>    </head>    <body translate=&quot;no&quot; >     <button type=&quot;button&quot; class=&quot;open-modal&quot;>Open modal</button>    <div class=&quot;modal-outer is-hidden&quot;>     <div class=&quot;modal-inner&quot;>       <button type=&quot;button&quot; class=&quot;modal-close&quot;>&amp;times;</button>       <h1>Modal content</h1>       <p>You can click anywhere outside the modal to close it.<br>Alternatively, there's the close button in the upper right corner.</p>     </div>    </div>       <script src=&quot;https://static.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js&quot;></script>         <script id=&quot;rendered-js&quot; >    (function () {     &quot;use strict&quot;;     var modal = document.querySelector(&quot;.modal-outer&quot;);     var open = document.querySelector(&quot;.open-modal&quot;);     var close = modal.querySelector(&quot;.modal-close&quot;);     function handleModalOpen() {       modal.classList.remove(&quot;is-hidden&quot;);     }     function handleModalClose() {       modal.classList.add(&quot;is-hidden&quot;);     }     function handleModalClick(evt) {       if (!evt.target.closest(&quot;.modal-inner&quot;)) {         handleModalClose();       }     }     function handleKeyDown(evt) {       if (evt.key == &quot;Escape&quot; &amp;&amp; !modal.classList.contains(&quot;is-hidden&quot;)) {         handleModalClose();       }     }     open.addEventListener(&quot;click&quot;, handleModalOpen);     close.addEventListener(&quot;click&quot;, handleModalClose);     modal.addEventListener(&quot;click&quot;, handleModalClick);     window.addEventListener(&quot;keydown&quot;, handleKeyDown);    })();    //# sourceURL=pen.js       </script>    </body>    </html>    " sandbox="allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups allow-presentation  allow-scripts allow-top-navigation-by-user-activation" allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; vr" allowTransparency="true" allowpaymentrequest="true" allowfullscreen="true">    </iframe>    </div>    </body>    </html>最后一个示例是另一个我可以肯定的组件:模态。模态的实现通常具有挑战性,因为它们需要提供许多功能,同时要易于访问和(理想情况下)美观。我们要重点关注如何关闭模式。在此示例中,可以通过按下Esc键盘,单击模态中的按钮或单击模态之外的任何位置来实现。在我们的JavaScript中,我们想听模式中某处的点击:var modal = document.querySelector(".modal-outer");modal.addEventListener("click", handleModalClick);默认情况下,通过.is-hidden实用程序类隐藏模式。只有当用户单击红色的大按钮时,模态才会通过删除此类而打开。一旦打开了模态,单击它的任何地方(关闭按钮除外)都不会无意间将其关闭。事件侦听器回调函数负责:function handleModalClick(evt) {  // `evt.target` is the DOM node the user clicked on.  if (!evt.target.closest(".modal-inner")) {    handleModalClose();  }}evt.target是被单击的DOM节点,在此示例中,是MODE后面的整个背景<divclass="modal-outer">。此DOM节点不在内<divclass="modal-inner">,因此Element.closest()可以使所有想要的气泡冒泡,而找不到它。条件将对此进行检查并触发handleModalClose功能。单击节点内的某个位置(例如标题),将成<divclass="modal-inner">为父节点。在这种情况下,条件不是真实的,将模式保留为打开状态。关于浏览器支持…与任何酷的“新”JavaScriptAPI一样,需要考虑对浏览器的支持。好消息是,Element.closest它并不是一个新事物,并且已经在相当长的一段时间内在所有主要浏览器中得到支持,支持率高达94%。我会说这可以在生产环境中安全使用。唯一不提供任何支持的浏览器是InternetExplorer(所有版本)。如果您必须支持IE,那么使用jQuery方法可能会更好。如您所见,有一些非常可靠的用例Element.closest。过去,像jQuery这样使我们相对容易使用的库现在可以与原始JavaScript一起使用。得益于良好的浏览器支持和易于使用的API,我在许多应用程序中都严重依赖此小方法,并且尚未感到失望。...

Web前端开发:JavaScript冒泡算法

Web前端开发:JavaScript冒泡算法。有一组数,依次比较两个相邻的数,如果他们的顺序(如从大到小或从小到大等)错误就把他们交换过来。我们先假设这一组数是有顺序的,那么我们找出它的规则。我们按照从小到大的顺序依次交换长方形,得到以下的结果。第一轮交换结果:CBAD   交换次数:3次第二轮交换结果:BACD   交换次数:3次第三轮交换结果:ABCD   交换次数:3次结果:1.比较轮数n-12.每次比较次数n-1简单的冒泡算法<script>var arr = [1,2,3,4];var temp = null;var m = null;var n = null;// 双重for循环for(var i=0;i<arr.length-1;i++){//指定交换论数和交换次数(内循环控制交换次数)    for(var a=0;a<arr.length-1;a++){        if(arr[a]<arr[a+1]){        //判断是否符合标准            temp = arr[a+1];            arr[a+1] = arr[a];            arr[a] = temp;        }        m++;    }    n++;}console.log(arr);console.log(m);console.log(n);</script>得到结果[4,3,2,1]    排序后9                     交换次数3                         轮数在上述的例子中,有重复交换的数据,我们再来分析下。第一轮交换:第一次:2134第二次:2314第三次:2341第二轮交换:第一次:3241第二次:3421第三次:3421第三轮交换:第一次:4321第二次:4321第三次:4321总结:每一轮都会比较出一个最大值或最小值,然后后一轮没有必要再比较了所以每比较一轮,就少比较一次。在第二轮的时候,有一个数不参与交换。在第三轮的时候,有两个数不参与交换。依次类推。所以,对上述代码优化。var arr = [1,2,3,4];var temp = null;var m = null;var n = null; // 双重for循环for(var i=0;i<arr.length-1;i++){    //指定交换论数和交换次数(内循环控制交换次数)    for(var a=0;a<arr.length-1-i;a++){         if(arr[a]<arr[a+1]){     //判断是否符合标准    temp = arr[a+1];    arr[a+1] = arr[a];    arr[a] = temp;    }    m++;    }    n++;}console.log(arr);console.log(m);console.log(n);得到结果。[4,3,2,1]排序后6交换次数3轮数再来个稍微复杂点的例子。<script>var arr = [66,22,23,39,77,25,88];var temp = null;var m = null;var n = null; // 双重for循环for(var i=0;i<arr.length-1;i++){//指定交换论数和交换次数(内循环控制交换次数)     for(var a=0;a<arr.length-1;a++){         if(arr[a]<arr[a+1]){     //判断是否符合标准    temp = arr[a+1];    arr[a+1] = arr[a];    arr[a] = temp;    }    m++;    }     n++; }console.log(arr);console.log(m);console.log(n);</script>结果:[88,77,66,39,25,23,22]21少交换了15次6结果其实已经提前完成,有重复交换次数。这次,我们加个判断,就是比较本次没有移动任何元素,那么说明已经完成结果。<script>var arr = [66,22,23,39,77,25,88,11,33,23];var temp = null;var m = null;var n = null;var flag = true; // 双重for循环for(var i=0;i<arr.length-1;i++){//指定交换论数和交换次数(内循环控制交换次数)     flag = true;     for(var a=0;a<arr.length-1-i;a++){         if(arr[a]<arr[a+1]){     //判断是否符合标准    temp = arr[a+1];    arr[a+1] = arr[a];    arr[a] = temp;    flag = false;    }    m++;    }     n++;    if(flag){        break;        }    } console.log(arr);console.log(m);console.log(n);</script>结果:[88,77,66,39,33,25,23,23,22,11]42少交换了39次7少交换了2轮...

JS实现Web前端提交功能

Web前端提交功能,主要效果:用户评论后,在合适位置弹出“评论成功”,2秒钟后自动消失,提示用户评论成功。一起来看下如何实现。{#评论成功提示#}<div class="popup_con" style="display: none; margin-left: 300px">    <div class="popup" >        <p style="color: red; font-size: 16px">评论成功!</p>    </div>    <div class="mask"></div> </div>// 评论成功提示定时器 // 定一定时器函数 function showSuccessMsg() {   $('.popup_con').fadeIn('fast', function () {     setTimeout(function () {       $('.popup_con').fadeOut('fast', function () {       });     }, 2000)   }); }  // 提交评论 $("#form_comment").submit(function (event) {   event.preventDefault();   var comment = $('#comment').val();   var data = {     "comment": comment   };   $.ajax({     url: "/task_mgm/taskinfo_comment=" + taskId,     type: "POST",     data: JSON.stringify(data),     contentType: "application/json", // 传给后端的数据类型     dataType: "json", // 接收后端的数据类型     success: function (resp) {       if (resp.error == 'OK') {              showSuccessMsg();              {#alert('评论成功');#}              $('#comment').val(''); //清空评论框            } else {              alert('评论失败');            }          }        })      })...

Web前端开发每日小结:使用Javascript元素无缝向左向上滚动

Web前端开发中,文字或者图片无缝滚动的功能经常会遇到,比如向左向上循环滚动、一条条的滚动、多条一起滚动等等效果,很多小伙伴们习惯用插件去实现,比如swiper等。想了解关于逐条或者单条滚动的童鞋可以看下之前分享的一篇文章:一起来玩玩基于JS的无缝滚动通用动画效果今天介绍下用原生javascript如何实现文字向上和向左无缝循环滚动。先来看下向左滚动的效果:向左滚动<!DOCTYPE html><html><head>    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    <title>js文字向左无缝滚动 - Web前端之家 https://www.jiangweishan.com</title></head><body>    <style type="text/css">        .qimo8        {            overflow: hidden;            width: 815px;        }        .qimo8 .qimo        {            /*width:99999999px;*/            width: 8000%;            height: 30px;        }        .qimo8 .qimo div        {            float: left;        }        .qimo8 .qimo ul        {            float: left;            height: 30px;            overflow: hidden;            zoom: 1;        }        .qimo8 .qimo ul li        {            float: left;            line-height: 30px;            list-style: none;        }        .qimo8 li a        {            margin-right: 10px;            color: #444444;        }    </style>    <div id="demo" class="qimo8">        <div class="qimo">            <div id="demo1">                <ul>                    <li><a href='https://www.jiangweishan.com'>Web前端之家</a></li>                    <li><a href='https://jiangweishan.com/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/'>前端开发工具</a></li>                    <li><a href='https://jiangweishan.com/VueJs/'>VUEJS</a></li>                </ul>            </div>            <div id="demo2">            </div>        </div>    </div>    <script type="text/javascript">        var demo = document.getElementById("demo");        var demo1 = document.getElementById("demo1");        var demo2 = document.getElementById("demo2");        demo2.innerHTML = document.getElementById("demo1").innerHTML;        function Marquee() {            if (demo.scrollLeft - demo2.offsetWidth >= 0) {                demo.scrollLeft -= demo1.offsetWidth;            }            else {                demo.scrollLeft++;            }        }        var myvar = setInterval(Marquee, 30);        demo.onmouseout = function () { myvar = setInterval(Marquee, 30); }        demo.onmouseover = function () { clearInterval(myvar); }    </script></body></html>可以预览看下效果。向上滚动<!DOCTYPE html><html><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>向上滚动 - Web前端之家 https://www.jiangweishan.com</title>    <style>        *{margin: 0;padding: 0}        #box{height: 140px; border: solid 1px; overflow: hidden;}    </style></head><body>    <div id="box">        <ul id="ul1">            <li>Web前端之家 https://www.jiangweishan.com</li>            <li>Web前端之家 https://www.jiangweishan.com</li>            <li>Web前端之家 https://www.jiangweishan.com</li>            <li>Web前端之家 https://www.jiangweishan.com</li>            <li>Web前端之家 https://www.jiangweishan.com</li>            <li>Web前端之家 https://www.jiangweishan.com</li>            <li>Web前端之家 https://www.jiangweishan.com</li>        </ul>        <ul id="ul2"></ul>    </div>    <script>        window.onload = roll(50);        function roll(t) {            var ul1 = document.getElementById("ul1");            var ul2 = document.getElementById("ul2");            var box = document.getElementById("box");            ul2.innerHTML = ul1.innerHTML;            box.scrollTop = 0;            var timer = setInterval(rollStart, t);            box.onmouseover = function () {                clearInterval(timer)            }            box.onmouseout = function () {                timer = setInterval(rollStart, t);            }        }        function rollStart() {            if (box.scrollTop >= ul1.scrollHeight) {                box.scrollTop = 0;            } else {                box.scrollTop++;            }        }    </script></body></html>大家看完两个DEMO,有没有发现写法有点不同,有对比才有伤害,有些开发经验的童鞋会发现,第二种写法会好些。您可以用第二种方法去实现第一种效果的,试试吧。...

Web前端开发:跨域XMLHttpRequest处理

常规网页可以使用 XMLHttpRequest 对象从远程服务器发送和接收数据,但是它们受同一原始策略的限制 。 内容脚本代表已将内容脚本注入其中的Web起源发起请求,因此,内容脚本也应遵循 相同的起源策略。(自Chrome73以来,内容脚本一直受CORB的限制,而自Chrome83以来,内容脚本一直受 CORS的限制。)扩展来源不受限制-在扩展的背景页面或前景选项卡中执行的脚本可以与起源以外的远程服务器进行对话,只要该扩展程序请求跨域权限。扩展原点每个正在运行的扩展都存在于其自己的单独安全源中。在不请求其他特权的情况下,扩展可以使用XMLHttpRequest来获取其安装中的资源。例如,如果扩展config.json在config_resources文件夹中包含名为的JSON配置文件,则扩展可以像以下方式检索文件的内容:var xhr = new XMLHttpRequest();xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);xhr.send();如果扩展程序尝试使用其自身以外的其他安全来源,例如https://www.google.com,则浏览器将不允许它,除非扩展程序已请求适当的跨域许可。请求跨域权限通过将主机或主机匹配模式(或两者)添加到清单文件的 权限部分 ,该扩展可以请求访问其来源之外的远程服务器。{  "name": "My extension",  ...  "permissions": [    "https://jiangweishan.com/"  ],  ...}跨域许可权值可以是完全限定的主机名,如下所示:“https://www.google.com/”“https://www.gmail.com/”或者它们可以是匹配模式,如下所示:“https://*.google.com/”“https://*/”匹配模式“https://*/”允许HTTPS访问所有可访问的域。请注意,此处的匹配模式与内容脚本匹配模式相似,但是主机后面的所有路径信息都将被忽略。还要注意,访问权限是由主机和方案授予的。如果扩展要对给定主机或一组主机同时进行安全和非安全的HTTP访问,则它必须分别声明权限:"permissions": [  "http://www.jiangweishan.com/",  "https://jiangweishan.com/"]安全注意事项避免跨站点脚本漏洞使用通过XMLHttpRequest检索的资源时,您的后台页面应注意不要成为跨站点脚本的牺牲品。具体来说,请避免使用危险的API,例如:var xhr = new XMLHttpRequest();xhr.open("GET", "https://api.example.com/data.json", true);xhr.onreadystatechange = function() {  if (xhr.readyState == 4) {    // WARNING! Might be evaluating an evil script!    var resp = eval("(" + xhr.responseText + ")");    ...  }}xhr.send();var xhr = new XMLHttpRequest();xhr.open("GET", "https://api.example.com/data.json", true);xhr.onreadystatechange = function() {  if (xhr.readyState == 4) {    // WARNING! Might be injecting a malicious script!    document.getElementById("resp").innerHTML = xhr.responseText;    ...  }}xhr.send();相反,请选择不运行脚本的更安全的API:var xhr = new XMLHttpRequest();xhr.open("GET", "https://api.example.com/data.json", true);xhr.onreadystatechange = function() {  if (xhr.readyState == 4) {    // JSON.parse does not evaluate the attacker's scripts.    var resp = JSON.parse(xhr.responseText);  }}xhr.send();var xhr = new XMLHttpRequest();xhr.open("GET", "https://api.example.com/data.json", true);xhr.onreadystatechange = function() {  if (xhr.readyState == 4) {    // innerText does not let the attacker inject HTML elements.    document.getElementById("resp").innerText = xhr.responseText;  }}xhr.send();限制内容脚本对跨域请求的访问当代表内容脚本执行跨域请求时,请小心防范可能试图假冒内容脚本的恶意网页。特别是,不允许内容脚本请求任意URL。考虑一个示例,其中扩展程序执行跨源请求以使内容脚本发现商品价格。一种(不安全的)方法是让内容脚本指定要由后台页面获取的确切资源。chrome.runtime.onMessage.addListener(    function(request, sender, sendResponse) {      if (request.contentScriptQuery == 'fetchUrl') {        // WARNING: SECURITY PROBLEM - a malicious web page may abuse        // the message handler to get access to arbitrary cross-origin        // resources.        fetch(request.url)            .then(response => response.text())            .then(text => sendResponse(text))            .catch(error => ...)        return true;  // Will respond asynchronously.      }    });chrome.runtime.sendMessage(    {contentScriptQuery: 'fetchUrl',     url: 'https://another-site.com/price-query?itemId=' +              encodeURIComponent(request.itemId)},    response => parsePrice(response.text()));在上面的方法中,内容脚本可以要求扩展读取该扩展可以访问的任何URL。恶意网页可能能够伪造此类消息并欺骗该扩展以提供对跨域资源的访问。而是,设计消息处理程序,以限制可以获取的资源。下方,itemId内容脚本仅提供,而不提供完整的URL。chrome.runtime.onMessage.addListener(    function(request, sender, sendResponse) {      if (request.contentScriptQuery == 'queryPrice') {        var url = 'https://another-site.com/price-query?itemId=' +            encodeURIComponent(request.itemId);        fetch(url)            .then(response => response.text())            .then(text => parsePrice(text))            .then(price => sendResponse(price))            .catch(error => ...)        return true;  // Will respond asynchronously.      }});chrome.runtime.sendMessage(    {contentScriptQuery: 'queryPrice', itemId: 12345},    price => ...);在HTTP上优先使用HTTPS此外,请特别注意通过HTTP检索的资源。如果您的扩展程序用于恶意网络,则网络攻击者(也称为“中间人”)可能会修改响应,并有可能攻击您的扩展程序。相反,请尽可能使用HTTPS。调整内容安全策略如果您通过在清单中添加属性来修改应用或扩展程序的默认内容安全策略,则 content_security_policy需要确保允许您要连接的任何主机。虽然默认策略不限制与主机的连接,但是在显式添加connect-srcor default-src 指令时要小心。...

Web前端开发:分享react里的form表单“秘方”

Web前端之家</title><scriptsrc="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script><scriptsrc="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script><scriptsrc="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script></head><body><divid="example"></div><scripttype="text/babel">classContentextendsReact.Component{render(){return<div><inputtype="text"value={this.props.myDataProp}onChange={this.props.updateStateProp}/><h4>{this.props.myDataProp}</h4></div>;}}classHelloMessageextendsReact.Component{constructor(props){super(props);this.state={value:'HelloWeb前端之家!'};this.handleChange=this.handleChange.bind(this);}handleChange(event){this.setState({value:event.target.value});}render(){varvalue=this.state.value;return<div><ContentmyDataProp={value}updateStateProp={this.handleChange}></Content></div>;}}ReactDOM.render(<HelloMessage/>,document.getElementById('example'));</script></body></html> 上面的代码将渲染出一个值为HelloRunoob!的input元素,并通过onChange事件响应更新用户输入的值。实例2在以下实例中我们将为大家演示如何在子组件上使用表单。onChange方法将触发state的更新并将更新的值传递到子组件的输入框的value上来重新渲染界面。你需要在父组件通过创建事件句柄(handleChange),并作为prop(updateStateProp)传递到你的子组件上。<!DOCTYPEhtml><html><head><metacharset="UTF-8"/><title>React实例-Web前端之家</title><scriptsrc="https://cdn.staticfile.org/react/15.4.2/react.min.js"></script><scriptsrc="https://cdn.staticfile.org/react/15.4.2/react-dom.min.js"></script><scriptsrc="https://cdn.staticfile.org/babel-standalone/6.22.1/babel.min.js"></script></head><body><divid="example"></div><scripttype="text/babel">varContent=React.createClass({render:function(){return<div><inputtype="text"value={this.props.myDataProp}onChange={this.props.updateStateProp}/><h4>{this.props.myDataProp}</h4></div>;}});varHelloMessage=React.createClass({getInitialState:function(){return{value:'HelloWeb前端之家!'};},handleChange:function(event){this.setState({value:event.target.value});},render:function(){varvalue=this.state.value;return<div><ContentmyDataProp={value}updateStateProp={this.handleChange}></Content></div>;}});ReactDOM.render(<HelloMessage/>,document.getElementById('example'));</script></body></html>Select下拉菜单在React中,不使用selected属性,而在根select标签上用value属性来表示选中项。<!DOCTYPEhtml><html><head><metacharset="UTF-8"/><title>React实例-Web前端之家!</title><scriptsrc="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script><scriptsrc="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script><scriptsrc="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script></head><body><divid="example"></div><scripttype="text/babel">classFlavorFormextendsReact.Component{constructor(props){super(props);this.state={value:'coconut'};this.handleChange=this.handleChange.bind(this);this.handleSubmit=this.handleSubmit.bind(this);}handleChange(event){this.setState({value:event.target.value});}handleSubmit(event){alert('Yourfavoriteflavoris:'+this.state.value);event.preventDefault();}render(){return(<formonSubmit={this.handleSubmit}><label>选择您最喜欢的网站<selectvalue={this.state.value}onChange={this.handleChange}><optionvalue="gg">Google</option><optionvalue="rn">Web前端之家</option><optionvalue="tb">Taobao</option><optionvalue="fb">Facebook</option></select></label><inputtype="submit"value="提交"/></form>);}}ReactDOM.render(<FlavorForm/>,document.getElementById('example'));</script></body></html>多个表单当你有处理多个input元素时,你可以通过给每个元素添加一个name属性,来让处理函数根据event.target.name的值来选择做什么。<!DOCTYPEhtml><html><head><metacharset="UTF-8"/><title>React实例-Web前端之家</title><scriptsrc="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script><scriptsrc="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script><scriptsrc="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script></head><body><divid="example"></div><scripttype="text/babel">classReservationextendsReact.Component{constructor(props){super(props);this.state={isGoing:true,numberOfGuests:2};this.handleInputChange=this.handleInputChange.bind(this);}handleInputChange(event){consttarget=event.target;constvalue=target.type==='checkbox'?target.checked:target.value;constname=target.name;this.setState({[name]:value});}render(){return(<form><label>是否离开:<inputname="isGoing"type="checkbox"checked={this.state.isGoing}onChange={this.handleInputChange}/></label><br/><label>访客数:<inputname="numberOfGuests"type="number"value={this.state.numberOfGuests}onChange={this.handleInputChange}/></label></form>);}}ReactDOM.render(<Reservation/>,document.getElementById('example'));</script></body></html>React事件以下实例演示通过onClick事件来修改数据:<!DOCTYPEhtml><html><head><metacharset="UTF-8"/><title>React实例-Web前端之家</title><scriptsrc="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script><scriptsrc="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script><scriptsrc="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script></head><body><divid="example"></div><scripttype="text/babel">classHelloMessageextendsReact.Component{constructor(props){super(props);this.state={value:'HelloRunoob!'};this.handleChange=this.handleChange.bind(this);}handleChange(event){this.setState({value:'Web前端之家'})}render(){varvalue=this.state.value;return<div><buttononClick={this.handleChange}>点我</button><h4>{value}</h4></div>;}}ReactDOM.render(<HelloMessage/>,document.getElementById('example'));</script></body></html>当你需要从子组件中更新父组件的state时,你需要在父组件通过创建事件句柄(handleChange),并作为prop(updateStateProp)传递到你的子组件上。实例如下:<!DOCTYPEhtml><html><head><metacharset="UTF-8"/><title>React实例-Web前端之家</title><scriptsrc="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script><scriptsrc="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script><scriptsrc="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script></head><body><divid="example"></div><scripttype="text/babel">classContentextendsReact.Component{render(){return<div><buttononClick={this.props.updateStateProp}>点我</button><h4>{this.props.myDataProp}</h4></div>}}classHelloMessageextendsReact.Component{constructor(props){super(props);this.state={value:'HelloRunoob!'};this.handleChange=this.handleChange.bind(this);}handleChange(event){this.setState({value:'Web前端之家'})}render(){varvalue=this.state.value;return<div><ContentmyDataProp={value}updateStateProp={this.handleChange}></Content></div>;}}ReactDOM.render(<HelloMessage/>,document.getElementById('example'));</script></body></html>总结关于react表单的内容就分享这么多,这些都是基本功能,希望能够帮助到大家,如果您有什么疑问都可以加群讨论哟。...

Web前端开发:探讨CSS3中元素位移、旋转、倾斜、缩放等方式

在前端开发项目中经常会遇到用CSS3实现各种旋转等方式,然后去实现一些动画效果,所以这方面的知识点还是得熟练掌握。众所周知,转换是CSS3中具有颠覆性的特征之一,可以实现元素的位移、旋转、倾斜、缩放,甚至支持矩阵方式,配合过渡和即将学习的动画知识,可以取代大量之前只能靠Flash才可以实现的效果。接下来我们逐个了解下,GOGOGO!!!移动translate(x,y)<!doctype html><html><head>    <meta charset="UTF-8">    <meta name="viewport"          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>Document</title>    <style>        div {            width: 100px;            height: 100px;            background-color: #ccc;            /*transform: translate(x, y);x  y  可以是负值*/            /*变形: 移动*/            transition: all 0.5s;/*当点击鼠标时,慢慢的移动,松开鼠标之后,慢慢的回去*/        }        div:active { /*  a:active鼠标点击没有松开的时候 触发的状态*/            transform: translate(50px, 50px);/*当点击之后,移动位置*/            /*transform: translate(50px);只移动x轴的情况这样写*/            /*transform: translate(0,50px);只移动y轴这样写*/            /*transform: translateX(50px);也可以这样写,而且一般这样写*/            /*transform: translateY(50px);也可以这样写*/        }    </style></head><body><div></div></body></html>案例:盒子水平垂直居中对齐的新写法<!doctype html><html><head>    <meta charset="UTF-8">    <meta name="viewport"          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>Document</title>    <style>        div {            width: 200px;            height: 200px;            background-color: #ccc;            /*transform: translate(100px);水平移动100*/            /*transform: translate(50%);这样写的话,移动的50%宽度是以他自身的宽度一半为准的,也就是div的一半,200px/2=100px,而不是以父级或浏览器的宽度的一半为准*/            /*以前我们定位的盒子居中对齐是先移动父级的一半宽度,再往回走自己宽度的一半,才能实现居中*/            /*以前的居中是这样写的*/             position: absolute;             left: 50%; /* !*以父级宽度为准*!*/             top: 50%;            /*margin-left: -100px;以前的居中是这样写的, 但是这样写的话就被写死了,父级宽度如果改动的话,这里也要改,他不会自动变*/           transform: translate(-50%, -50%); /*现在不需要再计算一半距离是多少,直接用50%就可以了*/        }    </style></head><body><div></div></body></html>缩放scale(x,y)transform:scale(0.8,1);这里如果括号里只写一个数的话,就不像translate(x,y)一样是默认为x值了,scale()括号里如果只有一个值的话,是xy一起共同都是这个值。scale(X,Y)使元素水平方向和垂直方向同时缩放(也就是X轴和Y轴同时缩放)scaleX(x)元素仅水平方向缩放(X轴缩放)scaleY(y)元素仅垂直方向缩放(Y轴缩放)scale()的取值默认的值为1,当值设置为0.01到0.99之间的任何值,作用使一个元素缩小;而任何大于或等于1.01的值,作用是让元素放大。<!doctype html><html><head>    <meta charset="UTF-8">    <meta name="viewport"          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>Document</title>    <style>        div {            width: 100px;            height: 100px;            background-color: #ccc;            margin: 100px auto;            transition: all 0.5s;/* 渐变 x  y 轴都变 */        }        div:hover {            transform: scale(1.2, 1.5); /* x 水平缩放 y垂直缩放 鼠标经过后放大,离开后复原,如果只有一个参数的话,宽度、高度都缩放,可以做一些网站的鼠标经过后图片放大的效果*/        }    </style></head><body><div></div></body></html>旋转rotate(deg)可以对元素进行旋转,正值为顺时针,负值为逆时针;<style>        img {            margin: 100px;            transition: all 0.5s;        }        img:hover {            transform: rotate(90deg);/*这里给 -90deg的话,就是逆时针旋转*/        }    </style></head><body><section>    <img src="images/chu.jpg" alt=""></section></body>上面例子拓展:<!doctype html><html><head>    <meta charset="UTF-8">    <meta name="viewport"          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>Document</title>    <style>        img {            margin: 100px;            transition: all 0.8s;/*过渡 所有条件都过渡*/            border-radius: 50%; /*给图片圆角*/            border: 5px solid cadetblue;        }        img:hover {            transform: rotate(360deg);/*旋转的度数写多少都可以,720都行*/        }    </style></head><body><section>    <img src="images/chu.jpg" alt=""></section></body></html>拓展练习:图片变成圆角,加边框,并且顺时针旋转360度。transform-origin 可以调整元素转换变形的原点,上面的图片旋转是围绕中心点旋转的,也可以设置为围绕其他点旋转<!doctype html><html><head>    <meta charset="UTF-8">    <meta name="viewport"          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>Document</title>    <style>        img {            width: 200px;            margin: 100px;            transition: all 0.6s;            transform-origin: top left;/*  origin:原点。transform-origin这个属性默认值是center  center 围绕中心点旋转,修改成top  left之后,他就会围绕左上角旋转 如果要精确一些的话,可以写成像素的:transform-origin: 20px 30px;*/        }        img:hover {            transform: rotate(180deg);        }    </style></head><body><img src="images/pk1.png" alt=""></body></html>倾斜skew(deg,deg)通过skew方法把元素水平方向上倾斜30度,处置方向保持不变。可以使元素按一定的角度进行倾斜,可为负值,第二个参数不写默认为0。<!doctype html><html><head>    <meta charset="UTF-8">    <meta name="viewport"          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">    <meta http-equiv="X-UA-Compatible" content="ie=edge">    <title>Document</title>    <style>        div {            font-size: 50px;            font-weight: 700;            margin: 100px;            transition: all 0.7s;        }        div:hover {            /*transform: skew(-30deg,0);*/            transform: skew(0, -10deg);/*y轴这个值如果大了,他会跑掉,离开鼠标能点到的地方又会跑回来*/        }    </style></head><body><div>楚乔</div></body></html>总结了解完这些基础CSS3变形的知识,我们可以去实现一些动画,会轻松很多了,赶紧去试试吧。...

Web前端开发:计算N次方的方法【**和math.pow()】

Web前端开发:计算N次方的方法【**和math.pow()】。一、指数运算符(**)DEMOconsole.log(2 ** 2); // 4console.log(3 ** 2); // 9console.log('3' ** '2'); // 9console.log((() => {    let x = 2;    let y = 3;    x **= y;    return x;})()); // 8二、Math.pow()定义Math.pow()方法返回基础的指数次幂。语法Math.pow(x, y)  //x:基数    y:指数DEMOconsole.log(Math.pow(2, 2)); // 4console.log(Math.pow(3, 2)); // 9console.log(Math.pow('3', '2')); // 9let x = 2;x = Math.pow(x, 3);console.log(x); // 8试试吧!...

Web前端开发:对于闭包和匿名函数的理解和应用

Web前端开发过程中,经常会用到闭包和匿名函数,不妨我们来梳理下如何使用,还有它们之间有什么区别。什么是匿名函数与匿名函数相对应的是具名函数,具名函数非常简单:functionmyFn(){},这就是个具名函数这个函数的name是myFn。可以测试一下:function myFn(){}cosnole.log(myFn.name);//myFn特别说明一下,函数表达式也是一种具名函数的定义方式。比如varmyFn1=function(){},打印myFn1.name,也会得到myFn1。再说匿名函数,一般用到匿名函数的时候都是立即执行的。通常叫做自执行匿名函数或者自调用匿名函数。常用来构建沙箱模式,作用是开辟封闭的变量作用域环境,在多人联合工作中,合并js代码后,不会出现相同变量互相冲突的问题。立即执行的匿名函数有很多种写法,常见的有以下两种:(function(){   console.log("我是匿名方式1");})();//我是匿名方式1 (function(){   console.log("我是匿名方式2");}());//我是匿名方式2 console.log((function(){}).name);//'' name为空两者的区别就是:一个是发起执行的括号在匿名函数括号的外面,另外一个发起执行的括号在匿名函数的里面。实际中的书写方式个人推荐第一种,这种写法更符合调用机制,调用时的参数也比较明显,如下:(function(i,j,k){   console.log(i+j+k);})(1,3,5);//9还有其他一些自执行匿名函数的写法,如下:function(){   console.log("我是匿名方式x");}();console.log(-function(){}.name);//-0+function(){   console.log("我是匿名方式x");}();console.log(+function(){}.name);//0~function(){   console.log("我是匿名方式x");}();console.log(~function(){}.name);//-1!function(){   console.log("我是匿名方式x");}();console.log(!function(){}.name);//truevoid function(){   console.log("我是匿名方式x");}();console.log(void function(){}.name);//undefined这几种操作符,有时会影响结果的类型,不推荐使用,大家可以查下资料看看各种方式之间的差别。具名函数其实也可以立即执行,在此不做太多的伸展(本文主要目的是为了说明匿名函数和闭包之间的关系)。实际上,立即执行的匿名函数并不是函数,因为已经执行过了,所以它是一个结果,这个结果是对当前这个匿名函数执行结果的一个引用(函数执行默认returnundefined)。这个结果可以是一个字符串、数字或者null/false/true,也可以是对象、数组或者一个函数(对象和数组都可以包含函数),当返回的结果包含函数时,这个立即执行的匿名函数所返回的结果就是典型的闭包了。闭包是怎么定义的,该如何理解闭包本身定义比较抽象,MDN官方上解释是:A closure is the combination of a function and the lexical environment within which that function was declared.中文解释是:闭包是一个函数和该函数被定义时的词法环境的组合。很多地方可以看到一个说法:js中每个函数都是一个闭包,这样理解也是没有问题的,不过会增加对闭包的理解难度,这里先不这么理解,可以按照闭包起的作用来理解它:就是能在一个函数外部执行这个函数内部的定义方法,并访问内部的变量在此,先看个经典的使用闭包的案例,实现在函数外部访问函数内部的局部变量:function box(){  var a = 10;  function inner(){    return a;  }  return inner;}var outer = box();console.log(outer());//10正常情况,box执行过后,会被回收机制回收所占用的内存,包括其内部定义的局部变量。但是此时box执行过后返回一个内部的函数inner,这个inner引用了内部的变量a,inner又被外部outer给接收,回收机制检查到内部的变量被引用,就不会执行回收。但是看到这里,还是一脸蒙比,哪里使用了闭包?貌似有三个函数呀,一个box,一个inner还有一个outer=box()。这个案例中用到的闭包其实是inner和inner被定义时的词法环境,这个闭包被return出来后被外部的outer引用,因此可以在box外部执行这个inner,inner能够读取到box内部的变量a。使用这个闭包的目的是为了在box外部访问a,就是通过执行outer()。javascript中闭包和匿名函数的区别闭包:值有权访问另一个函数作用域的变量的函数匿名函数:没有名字的函数区别:匿名函数不涉及跨作用域的问题,只是一种函数的简单类型,而匿名函数常见的是在函数内部创建一个新的函数。用匿名函数实现闭包上面的例子是在具名函数box内部用一个具名函数inner实现了闭包,那怎么使用匿名函数实现闭包呢,也很简单://第一步直把内部inner这个具名函数改为匿名函数并直接return, 结果同样是10function box(){  var a = 10;  return function(){    console.log(a) ;   }}var outer = box();outer();//10//第二步把外部var outer = box()改成立即执行的匿名函数var outer = (function(){  var a=10;  return function(){    console.log(a);  }})();//outer 作为立即执行匿名函数执行结果的一个接收,这个执行结果是闭包,outer等于这个闭包。//执行outer就相当于执行了匿名函数内部return的闭包函数//这个闭包函数可以访问到匿名函数内部的私有变量a,所以打印出10outer();//10这样我们就改写成了由匿名函数实现的闭包,真正使用到的闭包是内部的被return的函数和这个函数所定义时的环境。由此可以说明:闭包跟函数是否匿名没有直接关系,匿名函数和具名函数都可以创建闭包。...

Web前端开发:JavaScript中的字符串的一些应用

字符串是可包括字母,数字,或符号的一个或多个字符的序列。JavaScript中的字符串是原始数据类型和不可变的,这意味着它们是不变的。由于字符串是我们显示和处理文本的方式,而文本是我们通过计算机进行交流和理解的主要方式,因此字符串是熟悉的最基本的编程概念之一。创建和查看字符串的输出在JavaScript中,有三种方法可以编写字符串-它们可以写在单引号(''),双引号("")或反引号(``)中。所使用的引用类型必须在两侧都匹配,但是可以在整个相同的脚本中使用所有三种样式。使用双引号和单引号的字符串实际上是相同的。由于单引号或双引号字符串没有约定或官方偏好,所有重要的是在项目程序文件中保持一致。'This string uses single quotes.';"This string uses double quotes.";创建字符串的第三种也是最新的方法称为模板文字。模板文字使用反引号(也称为重音符号),其工作方式与常规字符串相同,只有一些附加奖励,我们将在本文中介绍。`This string uses backticks.`;查看字符串输出的最简单方法是将其打印到控制台,其中包括console.log():console.log("This is a string in the console.");OutputThis is a string in the console.输出值的另一种简单方法是使用以下命令向浏览器发送警报弹出窗口alert():alert("This is a string in an alert.");alert()是一种不常用的测试和查看输出的方法,因为关闭警报很快就会变得乏味。在变量中存储字符串在JavaScript中的变量命名的存储值的容器,使用的关键字var,const或let。我们可以将字符串的值赋给命名变量。const newString = "This is a string assigned to a variable.";既然newString变量包含我们的字符串,我们可以引用它并将其打印到控制台。console.log(newString);这将输出字符串值。OutputThis is a string assigned to a variable.通过使用变量代表字符串,我们不必在每次要使用它时重新键入字符串,这使我们更容易在程序中使用和操作字符串。字符串连接连接意味着将两个或多个字符串连接在一起以创建新字符串。为了连接,我们使用由+符号表示的连接运算符。当与算术运算一起使用时,该+符号也是加法运算符。让我们创建一个简单的连接实例,在"Sea"和之间"horse"。"Sea" + "horse";OutputSeahorse连接将字符串端到端地连接起来,将它们组合起来并输出一个全新的字符串值。如果我们想有词之间的空间Sea和horse,我们需要包括在字符串中的一个空白字符:"Sea " + "horse";OutputSea horse我们使用串联连接包含字符串值的字符串和变量。const poem = "The Wide Ocean";const author = "Pablo Neruda";const favePoem = "My favorite poem is " + poem + " by " + author ".";OutputMy favorite poem is The Wide Ocean by Pablo Neruda.当我们通过连接组合两个或多个字符串时,我们正在创建一个我们可以在整个程序中使用的新字符串。字符串中的变量与模板文字模板文字功能的一个特殊功能是能够在字符串中包含表达式和变量。我们可以使用${}语法来插入变量,而不必使用连接。const poem = "The Wide Ocean";const author = "Pablo Neruda";const favePoem = `My favorite poem is ${poem} by ${author}.`;OutputMy favorite poem is The Wide Ocean by Pablo Neruda.我们可以看到,在模板文字中包含表达式是实现相同结果的另一种方法。在这种情况下,使用模板文字可能更容易编写并且更方便。字符串文字和字符串值您可能会注意到我们在源代码中编写的字符串包含在引号或反引号中,但实际打印输出不包含任何引号。"Beyond the Sea";OutputBeyond the Sea在提及这些中的每一个时都有区别。甲文本字符串是因为它是写在源代码,包括报价的字符串。一个字符串值,就是我们在输出中看到,并且不包括报价。在上面的示例中,"BeyondtheSea"是一个字符串文字,并且BeyondtheSea是一个字符串值。逃避字符串中的行情和撇号由于引号用于表示字符串,因此在字符串中使用撇号和引号时必须特别注意。例如,尝试在单引号字符串的中间使用撇号将结束字符串,JavaScript将尝试将剩余的预期字符串解析为代码。我们可以通过尝试在I'm下面的收缩中使用撇号来看到这一点:const brokenString = 'I'm a broken string';console.log(brokenString);Outputunknown: Unexpected token (1:24)同样适用于尝试在双引号字符串中使用引号。为了避免在这些情况下抛出错误,我们可以使用一些选项:相反的字符串语法逃避角色模板文字我们将在下面探讨这些选项。使用备用字符串语法解决可能损坏的字符串的孤立情况的简单方法是使用您当前使用的字符串语法。例如,用字符串构建的字符串中的撇号"。"We're safely using an apostrophe in double quotes."用字符串构建的字符串中的引号'。'Then he said, "Hello, World!"';在我们组合单引号和双引号的方式中,我们可以控制字符串中引号和撇号的显示。但是,当我们在项目编程文件中使用一致的语法时,这可能很难在整个代码库中维护。使用转义字符(\)我们可以使用反斜杠(\)转义字符来阻止JavaScript将引用解释为字符串的结尾。语法\'总是单引号,语法\"总是双引号,不用担心会破坏字符串。使用这种方法,我们可以在用它构建的字符串中使用撇号"。'We\'re safely using an apostrophe in single quotes.'我们也可以在用它构建的字符串中使用引号"。"Then he said, \"Hello, World!\"";这种方法看起来有点麻烦,但您可能需要在同一个字符串中同时使用撇号和引号,这将使转义成为必要。使用模板文字模板文字是用反引号定义的,因此可以安全地使用引号和撇号,而无需任何额外的转义或考虑。`We're safely using apostrophes and "quotes" in a template literal.`;除了防止字符转义和允许嵌入式表达式的需要之外,模板文字也提供多行支持,我们将在下一节中讨论。使用交替的字符串语法,使用转义字符和使用模板文字,有几种方法可以安全地创建字符串。长串和换行符有时您可能希望在字符串中插入换行符或回车符。的\n或\r转义字符可用于在插入的代码的输出换行。const threeLines = "This is a string\nthat spans across\nthree lines.";OutputThis is a stringthat spans acrossthree lines.这在技术上可以使我们的输出在多行上。但是,在一行上写一个很长的字符串将很快变得非常难以阅读和使用。我们可以使用连接运算符在多行上显示字符串。const threeLines = "This is a string\n" +"that spans across\n" +"three lines.";我们可以使用\转义字符来转义换行符,而不是连接多个字符串。const threeLines = "This is a string\n\that spans across\n\three lines.";注意:此方法不是首选方法,因为它可能会导致某些浏览器和缩小器出现问题。为了使代码更具可读性,我们可以使用模板文字字符串。这消除了对包含换行符的长字符串进行连接或转义的需要。字符串以及换行符将被保留。const threeLines = `This is a stringthat spans acrossthree lines.`;OutputThis is a stringthat spans acrossthree lines.重要的是要了解创建跨越多行的换行符和字符串的所有方法,因为不同的代码库可能使用各种标准。结论在本文中,我们介绍了在JavaScript中使用字符串的基础知识,使用单引号和双引号创建和显示字符串文字,创建模板文字,连接,转义以及将字符串值分配给变量。...

Web前端vue开发:在表单验证中输入姓名时引发的“奇案”

近期一直再忙于vue.js的项目开发,尤其是表单功能;今晚想抽出一个小技术点来说说。比如:输入姓名的表单验证系列问题。对于姓名的验证,我们会想到几点:1、用户只能输入中文2、如果用户输入了非中文如何处理3、用户从表单外复制非中文进来如何处理4、限制输入的字数接下来我们以一个实例来说明下,如何去寻找解决方法,输入姓名的表单验证系列问题这是一个存款案例,用银行转账的时候要求把中文姓名提交给后台去,然后把姓名的参数给第三方业务接口用。我自己做了2个方案:方案一HTML代码:<Input v-model="name" placeholder="请输入真实姓名(中文)" style="width:auto;" clearable @keyup.native="inputChange($event)" @keydown.native="inputChange($event)"/>html这里不多说,很简单的,我们主要介绍下JS如何操作。inputChange(e) {      const o = e.target;      o.value = o.value.replace(/[^\u4E00-\u9FA5]/g, ''); // 清除除了中文以外的输入的字符   this.name = o.value;   },上述的方法,看上去没什么问题的,我们定义点击、松开的两个状态,然后对于input绑定v-model的name值进行正则中文处理。不知道大家想到问题没?其实这种方法,对于PC是可以的,如果对于移动端,大家可以去试试,发现问题就来了,点击可能出现无反应,尤其是对于ios这种比较挑的系统,各种BUG都来了。总结下缺点:keyup动作时验证:移动端兼容性不好clipboard粘贴时验证:浏览器安全策略限制读取设备粘贴板,功能有限。input动作时验证:不兼容IE,需要浏览器查询。对组件不友好,未达到需求效果。方案二那么我们如何去优化呢?让它也可以适用于移动端,于是寻找了另外的方法,进行优化,废话不多说,上代码:HTML代码:<!-- chinese name --><div class="pay-chinesename" v-if="showChineseName">    <input type="text" class="chinaName" placeholder="请填写真实姓名(中文)" @focus="checkStart" @blur="checkEnd" v-model="chineseName" name="chineseName" /></div>大家发现我们这里,把点击和松开的状态换成了focus和blur,其他的基本一样。当然JS做了不一样的处理。// chinsename checkcheckChinese(){    this.chineseName=this.chineseName.replace(/[^\u4E00-\u9FA5]/g, '')},checkStart(){    this.checkInterval=setInterval(this.checkChinese,100)},checkEnd(){    clearInterval(this.checkInterval)    console.log('chineseName:'+this.chineseName);}checkStart和checkEnd分别定义输入和离开时的状态的方法。主要逻辑:获取焦点时设置定时器,开始轮询验证,失去焦点后清除定时器。设置的是0.1秒验证一次,每次验证将输入框内容不符合正则的字符去除。验证间隔可以自己设置更短。获取焦点时开始验证,失去焦点后停止验证。看上去是不是感觉思维更加缜密了,当然看归看,我们得去试下才行,这里就不弄DEMO了。总结下优点:此方法有着极高的兼容性,通用性,textarea也适用。适用事件为经典事件,几乎所有框架,相关组件都可以使用,基本能兼容各种浏览器,设备。差点遗漏了一点,限制输入文字的长度,我觉得这个非常简单,用length方法即可。OK,已经有点晚了,就说这么多吧,最后简单总结下吧。总结上面列举的两种方案,第一种是我最开始使用的,第二种方案是从网上搜集到的,说实话,有时候只有对比才有“伤害”,跟我们平时购物一样,你没用过,你根本不知道优劣之分。话说回来,表单验证方法还有很多,比如插件和框架提供的方法等等,如果更好的方法都可以分享出来,留言或者QQ,不吝赐教。...

Web前端开发:介绍下三种页面之间的传值方法

Web前端开发:介绍下三种页面之间的传值方法,一起来了解下吧。JavaScript是一种属于网络的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常JavaScript脚本是通过嵌入在HTML中来实现自身的功能的。JavaScript一个页面向另一个页面传值的方法:1、通过URL进行传值。把要传递的信息接在URL上。Post.htm<input type="text" name="username"><input type="text" name="sex"><input type="button" value="Post"><script language="javascript" >function Post(){//单个值 Read.htm?username=baobao;//多全值 Read.htm?username=baobao&sex=male;  url = "Read.htm?username="+escape(document.all.username.value);url += "&sex=" + escape(document.all.sex.value);location.href=url;}</script>Read.htm<script language="javascript" >/**--------------- Read.htm -----------------* Request[key]* 功能:实现ASP的取得URL字符串,Request("AAA")* 参数:key,字符串.* 实例:alert(Request["AAA"])*--------------- Request.htm -----------------*/var url=location.search;var Request = new Object();if(url.indexOf("?")!=-1){var str = url.substr(1) //去掉?号  strs = str.split("&");for(var i=0;i<strs.length;i++){   Request[strs[i ].split("=")[0]]=unescape(strs[ i].split("=")[1]);}}alert(Request["username"])alert(Request["sex"])</script><script language="JavaScript"><!--function Request(strName){var strHref = "https://jiangweishan.com/index.htm?a=1&b=1&c=测试测试";var intPos = strHref.indexOf("?");var strRight = strHref.substr(intPos + 1);var arrTmp = strRight.split("&");for(var i = 0; i < arrTmp.length; i++){var arrTemp = arrTmp[i ].split("=");if(arrTemp[0].toUpperCase() == strName.toUpperCase()) return arrTemp[1];}return "";}alert(Request("a"));alert(Request("b"));alert(Request("c"));//--></script><script>String.prototype.getQuery = function(name){var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");var r = this.substr(this.indexOf("?")+1).match(reg);if (r!=null) return unescape(r[2]); return null;}var str ="https://jiangweishan.com/index.htm?a=1&b=1&c=测试测试";alert(str.getQuery("a"));alert(str.getQuery("b"));alert(str.getQuery("c"));</script>优点:取值方便.可以跨域.缺点:值长度有限制二、JavaScript静态页面值传递之Cookie篇Cookie是浏览器存储少量命名数据.它与某个特定的网页或网站关联在一起.Cookie用来给浏览器提供内存,以便脚本和服务器程序可以在一个页面中使用另一个页面的输入数据.Post.htm<input type="text" name="txt1"><input type="button" value="Post"><script language="javascript" >function setCookie(name,value){/**--------------- setCookie(name,value) -----------------* setCookie(name,value)* 功能:设置得变量name的值* 参数:name,字符串;value,字符串.* 实例:setCookie('username','baobao')*--------------- setCookie(name,value) -----------------*/var Days = 30; //此 cookie 将被保存 30 天  var exp = new Date();exp.setTime(exp.getTime() + Days*24*60*60*1000);document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString();location.href = "Read.htm"; //接收页面.}</script>Read.htm<script language="javascript" >function getCookie(name){/**--------------- getCookie(name) -----------------* getCookie(name)* 功能:取得变量name的值* 参数:name,字符串.* 实例:alert(getCookie("baobao"));*--------------- getCookie(name) -----------------*/var arr = document.cookie.match(new RegExp("(^| )"+name+"=([^;]*)(;|$)"));if(arr !=null) return unescape(arr[2]); return null;}alert(getCookie("baobao"));</script>优点:可以在同源内的任意网页内访问.生命期可以设置.缺点:值长度有限制.三:JavaScript使用Window.open方法进行静态页面值传递这两窗口之间存在着关系.父窗口parent.htm打开子窗口son.htm子窗口可以通过window.opener指向父窗口.这样可以访问父窗口的对象。Post.htm<input type=text name=maintext><input type=button value="Open">Read.htm<script language="javascript" >//window.open打开的窗口.//利用opener指向父窗口.var parentText = window.opener.document.all.maintext.value;alert(parentText);</script>优点:取值方便.只要window.opener指向父窗口,就可以访问所有对象。不仅可以访问值,还可以访问父窗口的方法.值长度无限制。缺点:两窗口要存在着关系.就是利用window.open打开的窗口.不能跨域。...

Web前端开发:关于vue中导入数组图片不能显示的解决方案

Web前端开发里关于vue种导入数组图片不能显示的解决方案。我们这里来举例来分析,会更清晰。先看段我弄的一个DEMO代码:自定义数组代码:data(){        return {                        items:[                {id:'1',count:'1',name:'苹果',price:'5',img:'../assets/img/1.png'},                {id:'2',count:'1',name:'李子',price:'6',img:'../assets/img/2.png'},                {id:'3',count:'1',name:'西红柿',price:'7',img:'../assets/img/3.png'},                {id:'4',count:'1',name:'葡萄',price:'8',img:'../assets/img/4.png'}                      ],                        active:true        }},模板代码:<template>    <div class="storedatebox">        <h2 class="hfon">VUE表模糊搜索和价格列表调整DEMO</h2>        <div class="store-search"><input type="text" name="" id="" placeholder="请输入产品名称"></div>        <table cellspacing="0" cellpadding="0" border="0" width="100%" class="tablelist">            <thead>                <tr>                    <th>序号</th>                    <th>产品</th>                    <th>图片</th>                    <th>数量</th>                    <th>单价</th>                    <th>总价</th>                    <th>选择</th>                </tr>            </thead>            <tbody>                <tr v-for='(item,index) in items' :key="index">                    <td>{{item.id}}</td>                    <td>{{item.name}}</td>                    <td><img src="item.img" alt=""></td>                    <td>                        <input :disabled='item.count == 0' type="button" value="-" @click='item.count--'>                        <span>{{item.count}}</span>                        <input type="button" value="+" @click='item.count++'>                    </td>                    <td>{{item.price | formatMoney(2)}}元</td>                    <td>{{item.price * item.count  |  formatMoney(3)}}元</td>                    <td><input type="checkbox" v-model="active"></td>                </tr>            </tbody>        </table>        <p class="data-info">{{totalCount}}件商品总计:{{totalPrice | formatMoney(3)}}元</p>    </div></template>我们这里有个表格需要遍历数据,发现图片显示不出来,如下图一般我们查找图片不显示的原因,会从图片的路径问题去排查,发现路径都是对的,也是不行。后续查了下官方API,需要对于数组内里的img_url加个require处理。代码如下:data(){        return {                        items:[                {id:'1',count:'1',name:'苹果',price:'5',img:require('../assets/img/1.png')},                {id:'2',count:'1',name:'李子',price:'6',img:require('../assets/img/2.png')},                {id:'3',count:'1',name:'西红柿',price:'7',img:require('../assets/img/3.png')},                {id:'4',count:'1',name:'葡萄',price:'8',img:require('../assets/img/4.png')}                         ],                        active:true        }},运行走起,发现还是没显示,此时此刻,有些童鞋就懵了。其实这些问题排除后,我们应该再去考虑遍历的时候,获取图的url方式是否正确,发现代码是这样的:<img src="item.img" alt="">其实这个是不对的,对于获取属性的时候,vue官方说明需要使用绑定元素属性的方法v-bind,所以我们需要改下这里的代码:<img :src="item.img" alt="">前面的分号就是v-bind的缩写,我们在运行下:问题终于解决了。那么我再问问大家,我把图片路径地址写成带有域名或者IP的,结果会如何。比如:{id:'1',count:'1',name:'苹果',price:'5',img:require('https://jiangweishan.com/zb_users/theme/Nobird_CMS_3/style/images/nlogo.png')},运行下结果:发现已经报错了,提示项目中找不到此图,要求我们去install;因此我们要避免这种方式要去调用图片,还是保存到项目文件夹中,再采用最上面的方法调用。总结关于vue中图片不能显示的问题,基本就这些,如您有问题可以加QQ群进行咨询。vue还有很多坑,慢慢踩,不断进步。...

Web前端开发:insertBefore()方法

insertBefore()是jQuery的一个内置的方法,用来在一个指定的元素之前插入一些HTML内容。语法:$(content).insertBefore(target)参数:● content:表示需要在指定目标之前插入的HTML内容。● target:表示指定目标。示例1:<!DOCTYPE html><html><head><meta charset="UTF-8"><script type="text/javascript" src="jquery.min.js"></script><script>$(document).ready(function() {$("button").click(function() {// insertBefore $("<div>Welcome to here!</div>").insertBefore("p");});});</script></head><body><div class="box"><p>Hello!</p><button>单击此处</button></div></body></html>示例2:<!DOCTYPE html><html><head><meta charset="UTF-8"><script type="text/javascript" src="jquery.min.js"></script><script>$(document).ready(function() {$("button").click(function() {$("<div>Welcome to here!</div>").insertBefore("p");});});</script></head><body><div class="box"><p>Hello!</p><p>Hi!</p><button>单击此处</button></div></body></html>...

Web前端开发中SVG在CSS里的玩法,您学会了吗?

Web前端之家</text>              </clipPath>            </defs>          </svg>                    <div class="area"></div>                <script>    </script></body></html>便携式SVG最后,独立的SVG可以包含CSS,JavaScript和base64编码的字体或位图图像!XML范围以外的任何内容都应包含在<![CDATA[… ]]>部分中。考虑以下invader.svg文件。它使用悬浮效果和修改viewBox状态的JavaScript动画定义CSS样式:<svg id="invader" xmlns="http://www.w3.org/2000/svg" viewBox="35.4 35.4 195.8 141.8">  <!-- invader images: https://github.com/rohieb/space-invaders !-->  <style>/* <![CDATA[ */    path {      stroke-width: 0;      fill: #080;    }    path:hover {      fill: #c00;    }  /* ]]> */</style>  <path d="M70.9 35.4v17.8h17.7V35.4H70.9zm17.7 17.8v17.7H70.9v17.7H53.2V53.2H35.4V124h17.8v17.7h17.7v17.7h17.7v-17.7h88.6v17.7h17.7v-17.7h17.7V124h17.7V53.2h-17.7v35.4h-17.7V70.9h-17.7V53.2h-17.8v17.7H106.3V53.2H88.6zm88.6 0h17.7V35.4h-17.7v17.8zm17.7 106.2v17.8h17.7v-17.8h-17.7zm-124 0H53.2v17.8h17.7v-17.8zm17.7-70.8h17.7v17.7H88.6V88.6zm70.8 0h17.8v17.7h-17.8V88.6z"/>  <path d="M319 35.4v17.8h17.6V35.4H319zm17.6 17.8v17.7H319v17.7h-17.7v17.7h-17.7V159.4h17.7V124h17.7v35.4h17.7v-17.7H425.2v17.7h17.7V124h17.7v35.4h17.7V106.3h-17.7V88.6H443V70.9h-17.7V53.2h-17.7v17.7h-53.2V53.2h-17.7zm88.6 0h17.7V35.4h-17.7v17.8zm0 106.2h-35.5v17.8h35.5v-17.8zm-88.6 0v17.8h35.5v-17.8h-35.5zm0-70.8h17.7v17.7h-17.7V88.6zm70.9 0h17.7v17.7h-17.7V88.6z"/>  <script>/* <![CDATA[ */    const      viewX = [35.4, 283.6],      animationDelay = 500,      invader = document.getElementById('invader');    let frame = 0;    setInterval(() => {      frame = ++frame % 2;      invader.viewBox.baseVal.x = viewX[frame];    }, animationDelay);  /* ]]> */</script></svg>在HTML <img>或CSS背景中引用时,SVG成为初始状态(本质上是第一个动画帧)的静态图像:但是,在其自己的浏览器选项卡中打开图像,所有效果都会返回。这对于分发需要一定程度的嵌入式交互性的图像,演示或小型文档可能很有用。复杂的SVGSVG在网页内外都提供了广泛的技术可能性。将CSS与SVG结合使用时,可以以有趣的方式对整个图像或单个元素进行样式设置和动画处理。本文介绍了处理SVG图像的方法,但是它们通常用于较小的视觉增强,例如:表单重点摘要和验证将汉堡菜单变成X关闭图标产生类似熔岩灯的变形。Web开发人员在探索通过将CSS与SVG结合使用来转换无聊的基于块的页面的方法,从而产生微妙的效果。如果您创建任何有趣的示例,都可以加群分享或者留言。...

Web前端开发:选择除指定元素以外的所有元素

在js中可以:not()选择器来选择除指定元素以外的所有元素,:not()选择器选择与给定元素不匹配的所有元素。:not()选择器中接受所有元素,例:not(diva)和:not(div,a)。语法:$(":not(selector)")参数:selector:用于指定不要选择的元素。selector参数接受任何类型的选择器。示例1:选择除了p.intro的所以p元素,并更改背景颜色<!DOCTYPE HTML><html><head><meta charset="UTF-8"></head><body style="text-align: center;"><p class="intro">Hello World!</p>         <p>jQuary :not选择器</p>         <p>改变背景色</p>         <button>改变color</button>        <script src="https://code.jquery.com/jquery-1.10.2.js"></script><script>         $(document).click(function() {             $("p:not(.intro)").css(               "background-color", "plum");         });     </script></body></html>示例2:<!DOCTYPE HTML><html><body><div><input type="checkbox" name="a"><span>Mary</span></div><div><input type="checkbox" name="b"><span>lcm</span></div><div><input type="checkbox" name="c" checked="checked"><span>Peter</span></div><script src="https://code.jquery.com/jquery-1.10.2.js"></script><script>$("input:not(:checked) + span").css("background-color", "yellow");$("input").attr("disabled", "disabled");</script></body></html>...

Web前端开发:用JS刷新页面

Web前端开发的小技巧:用JS刷新页面,介绍几种方法。方法1:使用location.reload()location.reload()方法重新加载当前web页面,此方法类似于你浏览器上的刷新页面按钮。如果把该方法的参数设置为true参数,可强制页面从服务器加载并忽略浏览器缓存。语法:location.reload(true)示例:<!DOCTYPE html><html><head><meta charset="utf-8"></head>  <body>    <h3 style="color: green">如何使用jQuery刷新页面?</h3>    <p>这是一段测试文本!</p>          输入文本:<input type="text" /><br /><br />          <button type="button">重新加载页面</button>    <script src="https://code.jquery.com/jquery-3.3.1.min.js"> </script>    <script type="text/javascript">        $(document).ready(function () {            $("button").click(function () {                location.reload(true);                alert('Reloading Page');            });        });    </script></body>  </html>方法2:使用history.go(0)history.go()方法根据传递给它的参数从浏览器历史记录中加载一个URL。如果传递的参数为“0”,则会重新加载当前页。语法:history.go(0);示例:<!DOCTYPE html><html><head><meta charset="utf-8"></head>   <body>     <h3 style="color: green">如何使用jQuery刷新页面?</h3>     <p>这是一段测试文本!</p>           输入文本:<input type="text" /><br /><br />          <button type="button">重新加载页面</button>     <script src="https://code.jquery.com/jquery-3.3.1.min.js"> </script>     <script type="text/javascript">         $(document).ready(function () {             $("button").click(function () {                 history.go(0);             });         });     </script> </body>   </html>...

Web前端开发人员常用的5款编辑器

Web前端开发项目中,怎么去选择合适自己的一款呢?需要大家自己去了解,现介绍目前常用的5种简洁轻便的编辑器。往下看吧!1、SublimeText(非开源)SublimeText是一个轻量、简洁、高效、跨平台的编辑器。SublimeText的特色功能:良好的扩展功能,官方称之为安装包(Package)。右边没有滚动条,取而代之的是代码缩略图,这个功能非常赞强大的快捷命令“可以实时搜索到相应的命令、选项、snippet和syntex,按下回车就可以直接执行,减少了查找的麻烦。”即时的文件切换。随心所欲的跳转到任意文件的任意位置。多重选择(Multi-Selection)功能允许在页面中同时存在多个光标。支持VIM模式支持宏,简单地说就是把操作录制下来或者自己编写命令,然后播放刚才录制的操作或者命令。2、VisualStudioCodeVisualStudioCode是一个运行于OSX,Windows和Linux之上的,针对于编写现代Web和云应用的跨平台编辑器。3、GithubAtomAtom是GitHub专门为程序员推出的一个跨平台文本编辑器。具有简洁和直观的图形用户界面,并有很多有趣的特点:支持CSS,HTML,JavaScript等网页编程语言。它支持宏,自动完成分屏功能,集成了文件管理器。4、BowPadBowPad是一个带有功能区UI的简单而快速的文本编辑器显著特性:超过100种文件类型和语言的语法高亮显示处理许多不同的编码,包括UTF-8、UTF-16甚至UTF-32垂直滚动条中的导航提示根据路径对打开的标签进行着色可用JScript或VBScript编写的插件扩展界面截图:5、EditraEditra是一个支持多平台的文本编辑器,可以支持基本语法和二十种语言。它使用方便,可以用颜色标注重点部分,支持进行内嵌式编辑,也可以进行代码编辑。总结其实还有很多其他的,比如webstorm等,说实话那些太臃肿了,又耗内层,所以如果在电脑配置不过的情况下,我们更多去选择更实用简便的编辑器。...

Web前端开发同学是不是应该少用if-else语句了呢?

Web前端开发同学是不是应该少用if-else语句了呢?if…else语句是许多程序员在写代码时最常用的方式之一。你甚至可以看到许多程序员的代码中嵌套着无数else语句。可这样,真的好吗?一篇关于卫语句的实用介绍。在刚开始接触编程时,我多希望能有人在我摸索着开发第一个网站的过程中,给我分享一点中肯的人生经验……那会儿我犯下的第一个大错误,就是在编写条件时过度使用else关键字。不只是我,后来我发现很多开发者朋友都有这个问题,所以我打算在今天的文章里好好聊聊这件事。免责声明:本文纯粹是我自己的主观感受。在某些情况下,我们可能没办法在代码里使用这类方法。有时候,使用else关键字确实是最好的解决方案。这些我都承认,本文只是想给大家提供一点关于构建逻辑的新思路。缩进波动拳!卫语句根据维基百科的介绍,卫语句是对完整性前提条件检查。用于避免在执行期间发生错误。望之不似人言,下面我就用普通话翻译翻译。我们首先得先对前提条件(在代码开头)进行完整性检查,这样才能避免主逻辑流程当中出现错误。在理想的流程中(当验证正确时),我们希望程序的主逻辑在验证之后才开始运行。现在让我们假设自己在运营一个网站,其中包含一个高级购买区域,仅限付费客户在每天夜里12点之后访问。if (\$user != null) {if (time() >= strtotime('12 pm')) {if (\$user->hasAccess(UserType.PREMIUM)) {if (\$store->hasItemsInStock()) {// the content a premium user user should be able to see when the store is in stock// after 12pm.} else {return 'We are completely sold out.';}} else {return 'You do not have premium access to our website.';}} else {return 'This section is not opened before 12PM';}} else {return 'You are not signed in.';}在实际应用中,我们可能会返回某种形式的异常。虽然这是一种条件流方案,但即使其中只包含少量else关键字,我们也很难跟上这样的条件思路。这还只是条件逻辑的一个简单示例。在实际场景当中,大家肯定遇到过对逻辑极为复杂的类进行导航的状况。在我看来,这样的编码方式缺乏可持续性,我们应该采取更好的办法。使用卫语句,我们可以遵循以下框架:if (condition1()) {return ...;}if (condition2()) {return ...;}// Input is valid.doSomething();利用这套框架,我们可以重构以前的代码,具体如下所示:if (\$user == null) {return 'You are not signed in.';}if (time() < strtotime('12 pm')) {return 'This section is not opened before 12PM';}if (!\$user->hasAccess(UserType.PREMIUM)) {return 'You do not have premium access to our website';}if (!\$store->hasItemsInStock()) {return 'We are completely sold out.';}// the content a premium user user should be able to see when the store is in stock// after 12pm.在卫语句中,我们通常将布尔表达式反转为我们想要assert的内容。如果我们希望用户在登录之后才能查看此页面,那么首先就得检查他们是否已经登录。这种方法实现了相同的精确逻辑流程,但在我看来,这体现出了更为清晰的条件逻辑处理方法。总结在编程时,我们应该始终牢记这样一个问题:“这些代码能够稳定执行至少6个月吗?”就当下来讲,这些代码也许能够很好地解决问题。但是未来呢?在编写代码时不考虑后续需求,显然是种愚蠢的行为。考虑到这一点,我果断放弃了那些已经修复过无数次的代码,从零开始编写功能。是的,这样才能彻底解决掉技术债务。适当运用卫语句,大家将能够为自己乃至团队的开发成果奠定坚实的基础,从而在未来需要时随时满足种种新增需求。...

Web前端工程师会“抛弃 React、Angular”吗?

Web前端工程师会“抛弃React、Angular”?时至2019年,我们都已达成了一项共识:组件可以构建快速、优雅且可维护的UI。问题在于,每个框架(例如ReactJs、AngularJS、Vue.js或其他一些较小的UI框架)在解决常见的问题时,都会使用自己的模式和解决方案。这些框架促进了可重用性,且易于使用。另外,我听说这些框架背后都有Google或Facebook等大公司的支持。在本文中,我们来讨论一下,这种说法是否属实,社区是否可以做的更好,以及我们是否有更好的选择。Web网站、Web应用程序,PWA或其他一切在浏览器中运行的代码最终都会化作HTML、CSS和JavaScript(也许还有WebAssembly)。那么,我们的目标是应该熟练地使用这些工具。我并没有说我们就使用这些工具,不要理会任何类型的库或框架。我们都应该使用,但如果我们的选择过多,那么会怎样?事实上,如今的选择确实过多!多到让你觉得有点头晕。这些工具非但没有加快你的速度,甚至还成为了累赘,因为你不知道应该使用哪个前端UI库。有时,你会想:“以后我就使用ReactJS”。这也不是不行。ReactJS是一种非常好的解决方案,但我们还有Angular以及其他UI框架。这意味着我们无法像一个社区那样协同工作,而是需要将自己分散到这些小社区中。尤其是当你发现其中大多数工具都缺乏我们日常使用的功能时,就会觉得更加糟糕。ReactJS中的Router一点也不好玩。表单验证也很没劲,没人愿意做。因此,我们需要在这些UI框架的基础上,再建立别的代码库,而且在大多数情况下,我们需要建立2-3库来执行这些操作。我们不仅需要在UI框架上花费心思,而且还要付出努力重新编写基本的代码。我们浪费了多少时间。可能有人看到这里会想,这似乎也算一件好事啊!真的吗?请搜索:“Linux桌面系统元年”。Linux桌面系统也有同样的问题:Gnome、KDE、XFCE、Cinnamon、Mate、LXDE等等。这些都在试图解决一个问题:改进Linux桌面系统。但他们成功了吗?接下来我们来谈一谈可重用性。有人记得从Angular1到Angular2的跳转吗?这两个版本就像两个完全不同的框架。现在我们有了Angular和AngularJS,它们一点都不令人困惑。你可能在想:“但是,ReactJS没有重大变化呀。”虽说如此,ReactJS的两个版本之间没有如此巨大的变化!但我问你,你敢在不使用钩子的地方发布React代码吗?相信评论中会有一半人说:“为什么不使用钩子?”在你需要将基于类的组件重写为基于函数的组件时,也会发生同样的情况。现在我问你一个问题,你必须如实回答,而且也不要冠冕堂皇地说:“我是编程高手,我想使用最新潮的技术”,而是让我们本着“解决实际问题并为人们提供解决方案”的态度。我的问题是:ReactJS的这些变化真的为你的客户带来了任何价值吗?对于你的用户呢?对于你的公司呢?代码的可阅读性提高了吗?如果如实回答,那么你可能会承认基于类的组件也很不错。仔细想一想,我们是不是被营销欺骗了?你可能想说,这与市场营销有什么关系?请不要忘记,是谁创建了ReactJS?是Facebook!那么又是谁创建了AngularJS?是谷歌。这两家公司最有名的是什么?如果你想说一家是社交网络,而另一家互联网搜索,那么你又错了!他们都是以广告和营销著称!如果你想了解一家公司真正的业绩,那就不应该看产品,而应该看他们的盈利点。我经常听到有人说:“某某框架背后有一家大公司的支持,所以这个框架一定不错”,我觉得你应该冷静下来仔细思考。这句话的意思是说,由于你使用的框架背后有一家拥有大量资金的公司的支持,所以这个框架不会在某一天消失。然而,谷歌是著名的项目杀手。人们还特意建立了一个网站来纪念被谷歌干掉的项目链接我希望你能看到Web开发社区目前遇到的一些问题。我们该如何解决?我个人认为,我们有现成的解决这些问题的正确方法。那就是制定正确的标准!W3C是一个优秀的组织,应该有更多来自社区的人参与其中。但这是另一个话题了。为什么标准可以帮助我们解决所有问题?当一项技术成为一项标准时,所有主流浏览器都会实现并使用这项标准。对开发人员来说,这意味着不需要额外的库,也不需要考虑其他浏览器中的边缘情况。即便有Bug或问题,也有相关责任者为所有用户修复Bug。因此,只需由一个人出面修改一次,而不需要成千上万的开发人员独自修改。这有助于解决社区分裂的问题。如果编写的某个组件可以同时在Vue.js、Angular和ReactJS中使用,那该多美好?这样更多的开发人员可以改进同一个Calendar组件,并创建优秀的组件,而不是创造出20个半成品的日历组件。如果这一切都不需要大公司的支持,只需社区和浏览器厂商支持就够了的话该多好?其实所有这些情况都曾经出现过,只不过我们现在忘记了!没错,我们确实忘记了!这项技术叫做“Web组件v1”。早在2014年,我们这个社区针对应该使用Web组件还是ReactJS的问题,发生过激烈的争论。最后,众所周知,我们选择了ReactJS。这在当时可能是正确的选择,因为Web组件还太年轻,而且规范还没有准备好。因此我们称之为Web组件v0,但自2018年以来我们现在有了v1。现在,所有大公司都接受了这个规范,并开始实施——极个别情况除外。此外,对于旧版本的浏览器还可以使用Polyfill。至于,Web组件v1的用法,以及以及如何将它们集成到当前项目中,这个话题我们日后再谈。...

浅析Web前端开发中的浏览器缓存机制

Web前端开发人员,刚开始开发项目的时候,往往会忽略“WEB缓存机制”的问题,对于浏览器的一些缓存机制,提醒大家需要花更多的时间去研究。缓存可以说是性能优化中简单高效的一种优化方式了。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。对于一个数据请求来说,可以分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。比如说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据。接下来的内容中我们将通过缓存位置、缓存策略以及实际场景应用缓存策略来探讨浏览器缓存机制。四种缓存介绍我们常常谈到的缓存主要分为四种:ServiceWorker,MemoryCache,DiskCache和PushCache1.ServiceWorkerServiceWorker是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用ServiceWorker的话,传输协议必须为HTTPS。因为ServiceWorker中涉及到请求拦截,所以必须使用HTTPS协议来保障安全。ServiceWorker的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。ServiceWorker实现缓存功能一般分为三个步骤:首先需要先注册ServiceWorker,然后监听到install事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。当ServiceWorker没有命中缓存的时候,我们需要去调用fetch函数获取数据。也就是说,如果我们没有在ServiceWorker命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从MemoryCache中还是从网络请求中获取的数据,浏览器都会显示我们是从ServiceWorker中获取的内容。2.MemoryCacheMemoryCache也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。一旦我们关闭Tab页面,内存中的缓存也就被释放了。那么既然内存缓存这么高效,我们是不是能让数据都存放在内存中呢?这是不可能的。计算机中的内存一定比硬盘容量小得多,操作系统需要精打细算内存的使用,所以能让我们使用的内存必然不多。当我们访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存。内存缓存中有一块重要的缓存资源是preloader相关指令(例如<linkrel="prefetch">)下载的资源。总所周知preloader的相关指令已经是页面优化的常见手段之一,它可以一边解析js/css文件,一边网络请求下一个资源。需要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control是什么值,同时资源的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type,CORS等其他特征做校验。3.DiskCacheDiskCache也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之MemoryCache胜在容量和存储时效性上。在所有浏览器缓存中,DiskCache覆盖面基本是最大的。它会根据HTTPHerder中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自DiskCache,关于HTTP的协议头中的缓存字段,我们会在下文进行详细介绍。浏览器会把哪些文件丢进内存中?哪些丢进硬盘中?关于这点,网上说法不一,不过以下观点比较靠得住:对于大文件来说,大概率是不存储在内存中的,反之优先当前系统内存使用率高的话,文件优先存储进硬盘4.PushCachePushCache(推送缓存)是HTTP/2中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。PushCache在国内能够查到的资料很少,也是因为HTTP/2在国内不够普及。这里推荐阅读JakeArchibald的HTTP/2pushistougherthanIthought这篇文章,文章中的几个结论:所有的资源都能被推送,并且能够被缓存,但是Edge和Safari浏览器支持相对比较差可以推送no-cache和no-store的资源一旦连接被关闭,PushCache就被释放多个页面可以使用同一个HTTP/2的连接,也就可以使用同一个PushCache。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签使用同一个HTTP连接。PushCache中的缓存只能被使用一次浏览器可以拒绝接受已经存在的资源推送你可以给其他域名推送资源如果以上四种缓存都没有命中的话,那么只能发起请求来获取资源了。那么为了性能上的考虑,大部分的接口都应该选择好缓存策略,通常浏览器缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置HTTPHeader来实现的。缓存过程分析浏览器与服务器通信的方式为应答模式,即是:浏览器发起HTTP请求–服务器响应该请求,那么浏览器怎么确定一个资源该不该缓存,如何去缓存呢?浏览器第一次向服务器发起该请求后拿到请求结果后,将请求结果和缓存标识存入浏览器缓存,浏览器对于缓存的处理是根据第一次请求资源时返回的响应头来确定的。具体过程如下图:协商缓存可以通过设置两种HTTPHeModified和ETag。1.Last-Modified和If-Modified-Since浏览器在第一次访问资源时,服务器返回资源的同时,在responseheader中添加Last-Modified的header,值是这个资源在服务器上的最后修改时间,浏览器接收后缓存文件和header;Last-Modified: Fri, 22 sep 2019 01:47:00 GMT浏览器下一次请求这个资源,浏览器检测到有Last-Modified这个header,于是添加If-Modified-Since这个header,值就是Last-Modified中的值;服务器再次收到这个资源请求,会根据If-Modified-Since中的值与服务器中这个资源的最后修改时间对比,如果没有变化,返回304和空的响应体,直接从缓存读取,如果If-Modified-Since的时间小于服务器中这个资源的最后修改时间,说明文件有更新,于是返回新的资源文件和200。但是Last-Modified存在一些弊端:如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成Last-Modified被修改,服务端不能命中缓存导致发送相同的资源因为Last-Modified只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源既然根据文件修改时间来决定是否缓存尚有不足,能否可以直接根据文件内容是否修改来决定缓存策略?所以在HTTP/1.1出现了ETag和If-None-Match2.ETag和If-None-MatchEtag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成。浏览器在下一次加载资源向服务器发送请求时,会将上一次返回的Etag值放到requestheader里的If-None-Match里,服务器只需要比较客户端传来的If-None-Match跟自己服务器上该资源的ETag是否一致,就能很好地判断资源相对客户端而言是否被修改过了。如果服务器发现ETag匹配不上,那么直接以常规GET200回包形式将新的资源(当然也包括了新的ETag)发给客户端;如果ETag是一致的,则直接返回304知会客户端直接使用本地缓存即可。3.两者之间对比:首先在精确度上,Etag要优于Last-Modified。Last-Modified的时间单位是秒,如果某个文件在1秒内改变了多次,那么他们的Last-Modified其实并没有体现出来修改,但是Etag每次都会改变确保了精度;如果是负载均衡的服务器,各个服务器生成的Last-Modified也有可能不一致。第二在性能上,Etag要逊于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值。第三在优先级上,服务器校验优先考虑Etag缓存机制强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified/If-Modified-Since和Etag/If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回304,继续使用缓存。具体流程图如下:到这里,不知道你是否存在这样一个疑问:如果什么缓存策略都没设置,那么浏览器会怎么处理?对于这种情况,浏览器会采用一个启发式的算法,通常会取响应头中的Date减去Last-Modified值的10%作为缓存时间。实际场景应用缓存策略1.频繁变动的资源Cache-Control:no-cache对于频繁变动的资源,首先需要使用Cache-Control:no-cache使浏览器每次都请求服务器,然后配合ETag或者Last-Modified来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。2.不常变化的资源Cache-Control:max-age=31536000通常在处理这类资源时,给它们的Cache-Control配置一个很大的max-age=31536000(一年),这样浏览器之后请求相同的URL会命中强制缓存。而为了解决更新的问题,就需要在文件名(或者路径)中添加hash,版本号等动态字符,之后更改动态字符,从而达到更改引用URL的目的,让之前的强制缓存失效(其实并未立即失效,只是不再使用了而已)。在线提供的类库(如jquery-3.3.1.min.js,lodash.min.js等)均采用这个模式。用户行为对浏览器缓存的影响所谓用户行为对浏览器缓存的影响,指的就是用户在浏览器如何操作时,会触发怎样的缓存策略。主要有3种:打开网页,地址栏输入地址:查找diskcache中是否有匹配。如有则使用;如没有则发送网络请求。普通刷新(F5):因为TAB并没有关闭,因此memorycache是可用的,会被优先使用(如果匹配的话)。其次才是diskcache。强制刷新(Ctrl+F5):浏览器不使用缓存,因此发送的请求头部均带有Cache-control:no-cache(为了兼容,还带了Pragma:no-cache),服务器直接返回200和最新内容。总结在性能优化的时候,缓存是非常重要的一环,所以我们应该多去学习这些方面技术知识,才能让我们开发的项目性能更好。...

Web前端开发小tips:JS正则表达式验证账号、手机号、电话和邮箱

Web前端小tips:JS正则表达式验证账号、手机号、电话和邮箱的写法,希望能给大家带来帮助验证帐号是否合法验证规则:字母、数字、下划线组成,字母开头,4-16位。function checkUser(str){ var re = /^[a-zA-z]\w{3,15}$/;    if(re.test(str)){        alert("正确");    }else{        alert("错误");    }          }checkUser("jihua_cnblogs");//调用验证手机号码验证规则:11位数字,以1开头。function  checkMobile(str) {    var  re = /^1\d{10}$/    if (re.test(str)) {        alert("正确");    } else {        alert("错误");    }}checkMobile('13800138000'); //调用checkMobile('139888888889');//错误示例验证电话号码验证规则:区号+号码,区号以0开头,3位或4位号码由7位或8位数字组成区号与号码之间可以无连接符,也可以“-”连接如01088888888,010-88888888,0955-7777777 function checkPhone(str){    var re = /^0\d{2,3}-?\d{7,8}$/;    if(re.test(str)){        alert("正确");    }else{        alert("错误");    }}checkPhone("09557777777");//调用验证邮箱验证规则:姑且把邮箱地址分成“第一部分@第二部分”这样第一部分:由字母、数字、下划线、短线“-”、点号“.”组成,第二部分:为一个域名,域名由字母、数字、短线“-”、域名后缀组成,而域名后缀一般为.xxx或.xxx.xx,一区的域名后缀一般为2-4位,如cn,com,net,现在域名有的也会大于4位function checkEmail(str){    var re = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/    if(re.test(str)){        alert("正确");    }else{        alert("错误");    }}checkEmail("contact@cnblogs.com");//调用...

Web前端常用性能优化的方法

Web前端常用性能优化的方法,一起来学习下吧!!从输入URL加载起看方向从输入URL到页面加载完成的过程(如果想了解更为详细的过程,请阅读JavaScript重文·也许你该知道浏览器输入URL后发生了什么?):    1、首先做DNS查询,如果这一步做了智能DNS解析的话,会提供访问速度最快的IP地址回来    2、接下来是TCP握手,应用层会下发数据给传输层,这里TCP协议会指明两端的端口号,然后下发给网络层。网络层中的IP协议会确定IP地址,并且指示了数据传输中如何跳转路由器。然后包会再被封装到数据链路层的数据帧结构中,最后就是物理层面的传输了    3、TCP握手结束后会进行TLS握手,然后就开始正式的传输数据    4、数据在进入服务端之前,可能还会先经过负责负载均衡的服务器,它的作用就是将请求合理的分发到多台服务器上,这时假设服务端会响应一个HTML文件    5、首先浏览器会判断状态码是什么,如果是200那就继续解析,如果400或500的话就会报错,如果300的话会进行重定向,这里会有个重定向计数器,避免过多次的重定向,超过次数也会报错    6、浏览器开始解析文件,如果是gzip格式的话会先解压一下,然后通过文件的编码格式知道该如何去解码文件    7、文件解码成功后会正式开始渲染流程,先会根据HTML构建DOM树,有CSS的话会去构建CSSOM树。如果遇到script标签的话,会判断是否存在async或者defer,前者会并行进行下载并执行JS,后者会先下载文件,然后等待HTML解析完成后顺序执行,如果以上都没有,就会阻塞住渲染流程直到JS执行完毕。遇到文件下载的会去下载文件,这里如果使用HTTP2.0协议的话会极大的提高多图的下载效率。    8、初始的HTML被完全加载和解析后会触发DOMContentLoaded事件    9、CSSOM树和DOM树构建完成后会开始生成Render树,这一步就是确定页面元素的布局、样式等等诸多方面的东西    10、在生成Render树的过程中,浏览器就开始调用GPU绘制,合成图层,将内容显示在屏幕上了我们从输入URL到显示页面这个过程中,涉及到网络层面的,有三个主要过程:DNS解析TCP连接HTTP请求/响应对于DNS解析和TCP连接两个步骤,我们前端可以做的努力非常有限。相比之下,HTTP连接这一层面的优化才是我们网络优化的核心。HTTP优化有两个大的方向:减少请求次数减少单次请求所花费的时间浏览器缓存策略浏览器缓存机制有四个方面,它们按照获取资源时请求的优先级依次排列如下:MemoryCacheServiceWorkerCacheHTTPCachePushCacheMemoryCacheMemoryCache,是指存在内存中的缓存。从优先级上来说,它是浏览器最先尝试去命中的一种缓存。从效率上来说,它是响应速度最快的一种缓存。浏览器秉承的是“节约原则”,我们发现,Base64格式的图片,几乎永远可以被塞进memorycache,这可以视作浏览器为节省渲染开销的“自保行为”;此外,体积不大的JS、CSS文件,也有较大地被写入内存的几率——相比之下,较大的JS、CSS文件就没有这个待遇了,内存资源是有限的,它们往往被直接甩进磁盘。ServiceWorkerCacheServiceWorker是一种独立于主线程之外的Javascript线程。它脱离于浏览器窗体,因此无法直接访问DOM。这样独立的个性使得ServiceWorker的“个人行为”无法干扰页面的性能,这个“幕后工作者”可以帮我们实现离线缓存、消息推送和网络代理等功能。我们借助Serviceworker实现的离线缓存就称为ServiceWorkerCache。HTTPCache它又分为强缓存和协商缓存。优先级较高的是强缓存,在命中强缓存失败的情况下,才会走协商缓存。对一链接get报文的基本缓存处理过程包括7个步骤:接收解析查询,缓存查看是否有本地副本可用,如果没有,就获取一份副本新鲜度检测,缓存查看已缓存副本是否足够新鲜,如果不是,就询问服务器是否有任何更新。创建响应,缓存会用新的首部和已缓存的主体来构建一条响应报文。发送,缓存通过网络将响应发回给客服端。日志强缓存强缓存是利用链接头中的Expires和Cache-Control两个字段来控制的。强缓存中,当请求再次发出时,浏览器会根据其中的expires和cache-control判断目标资源是否“命中”强缓存,若命中则直接从缓存中获取资源,不会再与服务端发生通信。是否足够新鲜时期:通过Expires:XXXXXXXXXXGMT(绝对日期时间链接或者Cache-Control:max-age=XXXX(相对日期时间链接Cache-Control相对于expires更加准确,它的优先级也更高。当Cache-Control与expires同时出现时,我们以Cache-Control为准。关键字理解public与private是针对资源是否能够被代理服务缓存而存在的一组对立概念。如果我们为资源设置了public,那么它既可以被浏览器缓存,也可以被代理服务器缓存;如果我们设置了private,则该资源只能被浏览器缓存。private为默认值。no-store与no-cache,no-cache绕开了浏览器:我们为资源设置了no-cache后,每一次发起请求都不会再去询问浏览器的缓存情况,而是直接向服务端去确认该资源是否过期(即走我们下文即将讲解的协商缓存的路线)。no-store比较绝情,顾名思义就是不使用任何缓存策略。在no-cache的基础上,它连服务端的缓存确认也绕开了,只允许你直接向服务端发送请求、并下载完整的响应。协商缓存协商缓存依赖于服务端与浏览器之间的通信。协商缓存机制下,浏览器需要向服务器去询问缓存的相关信息,进而判断是重新发起请求、下载完整的响应,还是从本地获取缓存的资源。如果服务端提示缓存资源未改动(NotModified),资源会被重定向到浏览器缓存,这种情况下网络请求对应的状态码是304。协商缓存的实现:从Last-Modified到Etag,详细自己百度,这里不再详细展开。HTTP缓存决策当我们的资源内容不可复用时,直接为Cache-Control设置no-store,拒绝一切形式的缓存;否则考虑是否每次都需要向服务器进行缓存有效确认,如果需要,那么设Cache-Control的值为no-cache;否则考虑该资源是否可以被代理服务器缓存,根据其结果决定是设置为private还是public;然后考虑该资源的过期时间,设置对应的max-age和s-maxage值;最后,配置协商缓存需要用到的Etag、Last-Modified等参数。PushCachePushCache是指HTTP2在serverpush阶段存在的缓存。PushCache是缓存的最后一道防线。浏览器只有在MemoryCache、HTTPCache和ServiceWorkerCache均未命中的情况下才会去询问PushCache。PushCache是一种存在于会话阶段的缓存,当session终止时,缓存也随之释放。不同的页面只要共享了同一个HTTP2连接,那么它们就可以共享同一个PushCache。CDN缓存,一个是回源。“缓存”就是说我们把资源copy一份到CDN服务器上这个过程,“回源”就是说CDN发现自己没有这个资源(一般是缓存的数据过期了),转头向根服务器(或者它的上层服务器)去要这个资源的过程。CDN往往被用来存放静态资源。所谓“静态资源”,就是像JS、CSS、图片等不需要业务服务器进行计算即得的资源。而“动态资源”,顾名思义是需要后端实时动态生成的资源,较为常见的就是JSP、ASP或者依赖服务端渲染得到的HTML页面。那“非纯静态资源”呢?它是指需要服务器在页面之外作额外计算的HTML页面。具体来说,当我打开某一网站之前,该网站需要通过权限认证等一系列手段确认我的身份、进而决定是否要把HTML页面呈现给我。这种情况下HTML确实是静态的,但它和业务服务器的操作耦合,我们把它丢到CDN上显然是不合适的。另外,CDN的域名必须和主业务服务器的域名不一样,要不,同一个域名下面的Cookie各处跑,浪费了性能流量的开销,CDN域名放在不同的域名下,可以完美地避免了不必要的Cookie的出现!图片优化二进制位数与色彩的关系在计算机中,像素用二进制数来表示。不同的图片格式中像素与二进制位数之间的对应关系是不同的。一个像素对应的二进制位数越多,它可以表示的颜色种类就越多,成像效果也就越细腻,文件体积相应也会越大。一个二进制位表示两种颜色(0|1对应黑|白),如果一种图片格式对应的二进制位数有n个,那么它就可以呈现2^n种颜色。计算图片大小对于一张100100像素的图片来说,图像上有10000个像素点,如果每个像素的值是RGBA存储的话,那么也就是说每个像素有4个通道,每个通道1个字节(8位=1个字节),所以该图片大小大概为39KB(100001*4/1024)。但是在实际项目中,一张图片可能并不需要使用那么多颜色去显示,我们可以通过减少每个像素的调色板来相应缩小图片的大小。了解了如何计算图片大小的知识,那么对于如何优化图片,想必大家已经有2个思路了:JPEG/JPG特点:有损压缩、体积小、加载快、不支持透明,JPG最大的特点是有损压缩。这种高效的压缩算法使它成为了一种非常轻巧的图片格式。另一方面,即使被称为“有损”压缩,JPG的压缩方式仍然是一种高质量的压缩方式:当我们把图片体积压缩至原有体积的50%以下时,JPG仍然可以保持住60%的品质。但当它处理矢量图形和Logo等线条感较强、颜色对比强烈的图像时,人为压缩导致的图片模糊会相当明显。PNG特点:无损压缩、质量高、体积大、支持透明,PNG(可移植网络图形格式)是一种无损压缩的高保真的图片格式。8和24,这里都是二进制数的位数。按照我们前置知识里提到的对应关系,8位的PNG最多支持256种颜色,而24位的可以呈现约1600万种颜色。PNG图片具有比JPG更强的色彩表现力,对线条的处理更加细腻,对透明度有良好的支持。它弥补了上文我们提到的JPG的局限性,唯一的BUG就是体积太大。SVG特点:文本文件、体积小、不失真、兼容性好,SVG(可缩放矢量图形)是一种基于XML语法的图像格式。它和本文提及的其它图片种类有着本质的不同:SVG对图像的处理不是基于像素点,而是是基于对图像的形状描述。Base64特点:文本文件、依赖编码、小图标解决方案,Base64并非一种图片格式,而是一种编码方式。Base64和雪碧图一样,是作为小图标解决方案而存在的。WebP特点:年轻的全能型选手,WebP像JPEG一样对细节丰富的图片信手拈来,像PNG一样支持透明,像GIF一样可以显示动态图片——它集多种图片文件格式的优点于一身。但是毕竟年轻,兼容性存在一些问题。渲染优化客户端渲染在客户端渲染模式下,服务端会把渲染需要的静态文件发送给客户端,客户端加载过来之后,自己在浏览器里跑一遍JS,根据JS的运行结果,生成相应的DOM。页面上呈现的内容,你在html源文件里里找不到——这正是它的特点。服务端渲染在服务端渲染的模式下,当用户第一次请求页面时,由服务器把需要的组件或页面渲染成HTML字符串,然后把它返回给客户端。页面上呈现的内容,我们在html源文件里也能找到。服务端渲染解决了一个非常关键的性能问题——首屏加载速度过慢,也解决了SEO搜索引擎的问题。浏览器渲染过程解析浏览器的渲染机制一般分为以下几个步骤:处理HTML并构建DOM树。处理CSS构建CSSOM树将DOM与CSSOM合并成一个渲染树。调用GPU绘制,合成图层,显示在屏幕上。在渲染DOM的时候,浏览器所做的工作实际上是:获取DOM后分割为多个图层对每个图层的节点计算样式结果(Recalculatestyle–样式重计算)将每个节点绘制填充到图层位图中(PaintSetup和Paint–重绘)图层作为纹理上传至GPU复合多个图层到页面上生成最终屏幕图像(CompositeLayers–图层重组)基于渲染流程的CSS优化建议CSS选择符是从右到左进行匹配的,比如#myListli{}实际开销相当高。避免使用通配符,只对需要用到的元素进行选择。关注可以通过继承实现的属性,避免重复匹配重复定义。少用标签选择器。如果可以,用类选择器替代。错误:#dataListli{}正确:.dataList{}不要画蛇添足,id和class选择器不应该被多余的标签选择器拖后腿。错误:.dataList#title正确:#title减少嵌套。后代选择器的开销是最高的,因此我们应该尽量将选择器的深度降到最低(最高不要超过三层),尽可能使用类来关联每一个标签元素。CSS的阻塞CSS是阻塞的资源。浏览器在构建CSSOM的过程中,不会渲染任何已处理的内容。即便DOM已经解析完毕了,只要CSSOM不OK,那么渲染这个事情就不OK。我们将CSS放在head标签里和尽快启用CDN实现静态资源加载速度的优化。JS的阻塞JS引擎是独立于渲染引擎存在的。我们的JS代码在文档的何处插入,就在何处执行。当HTML解析器遇到一个script标签时,它会暂停渲染过程,将控制权交给JS引擎。JS引擎对内联的JS代码会直接执行,对外部JS文件还要先获取到脚本、再进行执行。等JS引擎运行完毕,浏览器又会把控制权还给渲染引擎,继续CSSOM和DOM的构建。DOM渲染优化先了解回流和重绘回流:当我们对DOM的修改引发了DOM几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。重绘:当我们对DOM的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。重绘不一定导致回流,回流一定会导致重绘。回流比重绘做的事情更多,带来的开销也更大。在开发中,要从代码层面出发,尽可能把回流和重绘的次数最小化。例子剖析进化一进化2事实上,考虑JS的运行速度,比DOM快得多这个特性。我们减少DOM操作的核心思路,就是让JS去给DOM分压。在DOMFragment中,DocumentFragment接口表示一个没有父级文件的最小文档对象。它被当做一个轻量版的Document使用,用于存储已排好版的或尚未打理好格式的XML片段。因为DocumentFragment不是真实DOM树的一部分,它的变化不会引起DOM树的重新渲染的操作(reflow),且不会导致性能等问题。进化三:进化四:当涉及到过万调数据进行渲染,而且要求不卡住画面,如何解决?如何在不卡住页面的情况下渲染数据,也就是说不能一次性将几万条都渲染出来,而应该一次渲染部分DOM,那么就可以通过requestAnimationFrame来每16ms刷新一次。window.requestAnimationFrame()方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。注意:若您想要在下次重绘时产生另一个动画画面,您的回调例程必须调用requestAnimationFrame()。EventLoop事件循环中的异步队列有两种:macro(宏任务)队列和micro(微任务)队列。常见的macro-task比如:setTimeout、setInterval、setImmediate、script(整体代码)、I/O操作、UI渲染等。常见的micro-task比如:process.nextTick、Promise、MutationObserver等。例子分析://task是一个用于修改DOM的回调setTimeout(task, 0)上面代码,现在task被推入的macro队列。但因为script脚本本身是一个macro任务,所以本次执行完script脚本之后,下一个步骤就要去处理micro队列了,再往下就去执行了一次render,必须等待下一次的loop。Promise.resolve().then(task)上面代码,我们结束了对script脚本的执行,是不是紧接着就去处理micro-task队列了?micro-task处理完,DOM修改好了,紧接着就可以走render流程了——不需要再消耗多余的一次渲染,不需要再等待一轮事件循环,直接为用户呈现最即时的更新结果。当我们需要在异步任务中实现DOM修改时,把它包装成micro任务是相对明智的选择。上面说了重绘与回流,Eventloop,但很多人不知道的是,重绘和回流其实和Eventloop有关。当Eventloop执行完Microtasks后,会判断document是否需要更新。因为浏览器是60Hz的刷新率,每16ms才会更新一次。然后判断是否有resize或者scroll,有的话会去触发事件,所以resize和scroll事件也是至少16ms才会触发一次,并且自带节流功能。判断是否触发了mediaquery更新动画并且发送事件判断是否有全屏操作事件执行requestAnimationFrame回调执行IntersectionObserver回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好更新界面以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行requestIdleCallback回调。...

Web前端开发者的新选择:Flutter

Flutter已经不仅仅可以运行在Android或者iOS上,还可以运行在Windows,MacOS,Linux,ChromeOS甚至是Web上。设计框架flutter移动架构Flutter使用多层架构,最底层是C++编写的引擎,之上是用Dart编写的多层框架。引擎通过dart:ui库暴露给上层框架,dart:ui只负责图片绘制,其他工作都由上层框架完成。大部分引擎代码使用C++编写,与特定平台相关的如插件系统等,使用平台对应的语言编写:iOS使用Objective-C,Android使用Java。flutterweb架构要使Flutter运行于Web之上,需要解决三个主要问题:1.编译Dart为JavaScript2.选择可运行于Web之上的功能,剔除与特定平台相关的模块3.选择合适的Web特性来实现Flutter的功能。为此,Flutter项目组实现了三种原型:纯组件层(widgets)、组件加自定义布局(Widgets+customlayout),以及构建全新的底层Web引擎。诸多尝试后,Flutter项目组发现只有第三种方式才能够保证真正跨平台,即为Flutter实现一个全新的Web引擎。实现Web引擎的实现大致需要三个步骤:构建组件、排版以及绘制。组件的构建过程相对简单直接,将组件载入内存,监控它们的状态变化,并把需要更新的数据输送到排版、绘制阶段即可。得益于Dart最新的super-mixin特性,所有Flutter组件和组件框架都几乎能被完美编译为JavaScript。排版阶段相对复杂,最大的挑战来自文字排版。Flutter使用段落(Paragraph)的layout方法完成文字排版,但Web端却没有相应的文字排版API。此问题的解决方法:将文本插入到htmldom中,先让浏览器完成排版,然后再读取回文本的高度、宽度等各种属性。最后的也是最复杂的绘制阶段。Web端可用的特性有HTML/CSS、Canvas、SVG和WebGL等,其中WebGL过于底层和复杂,而纯HTML/CSS的实现又要求改动Flutter现有的API。项目组还在积极研究此阶段的最优方案,目前的候选方案有两种:HTML+CSS+Canvas和CSSPaintAPI。下面简要概括这两种方案的原理:HTML+CSS+Canvas-Flutter上层框架会生成大量层(Layer3),其中简单的图片层使用HTML+CSS绘制,且称之为DomCanvas实现;复杂的图片层则使用2DCanvas绘制,并称之为BitmapCanvas实现(浏览器底层使用Bitmap实现canvas)。除图片(Picture)外的其他层,会被转换为自定义html标签表示,比如:Opacity会被转换为<flt-opacity>。这样,Flutter页面就被转换为由html标签、DomCanvas以及BitmapCanvas组成的树状结构。这种实现方式在所有现代浏览器都可以正常工作,是目前首选的方案。转换前:转换后:CSSPaintAPI-CSSPaint10是众多浏览器厂商合作设计的Web端新API,它的主要特性是使普通的Html元素具备绘图能力,它底层不使用Bitmap,效率高而且不阻塞JavaScript线程。借助此API,可以直接使用<p>和<span>并配合自定义CSS属性完成绘制。CSSPaintAPI还比较新,不是所有浏览器都支持,但它应该是未来FlutterWeb的完美实现方案之一。其他FlutterWeb应用可以调用所有已有的DartWeb库,而且支持使用dart:js和package:js与JavaScript交互。出于性能以及可移植性考虑,FlutterWeb应用目前不支持直接使用CSS,即跟原生Flutter一样,全部使用组件代码来实现样式。FlutterWeb应用暂不支持嵌入到其他Web应用中,但可能会使用iframe或ShadowDom方式实现。其他Web组件如:html自定义标签、React组件、Angular组件等,都还不能嵌入FlutterWeb应用中。主要问题是这些组件会引入CSS,可能带来不可预知的影响,项目组还在研究应对方案。FlutterWeb版将保证最大的可移植性,但并不意味所有Flutter应用都不用修改就能运行于Web之上,比如:使用了ARCore等Web端不具备的功能的应用。安装更新flutter需要1.5.4以上的版本flutterchannelmaster&&flutterupgrade检查版本flutterdoctor把.pub-cache/bin加入PATH中比如windows:C:\Users\<your-username>\AppData\Roaming\Pub\Cache\binmac或linux$FLUTTER_HOME/.pub-cache/bin安装webdev$flutterpubglobalactivatewebdevwebdev--help实践强烈推荐VisualStudioCode开发工具,轻量而且强大,支持各种语言开发,做一些代码验证特别方便。以下都是用code操作,如果用androidstudio是类似的。创建新项目前提是安装了flutter和dart插件,这个就略过了,大家自己安装。打开命令Ctrl+Shift+P控制,找到选择Flutter:NewWebProject,如果没有,则是插件没有安装好。然后等待输入项目名字回车即可。打开菜单的Debug,选择StartDebugging,选择Dart&flutter即可,等待构建完成,会打开默认浏览器看到项目运行。运行已有的项目谷歌已经把gallery示例运行到web上,可以看到效果还是很惊人的,后面有资料有链接可以详细参考。下面运行galllery步骤:把代码下载下链接进入到仓库的gallery目录cdexamples/hello_world/更新包flutterpubupgrade本地运行webdevserve如果提示webdev需要升级,可以再次执行下面的命令即可:flutterpubglobalactivatewebdev...

Web前端TIPS:循环中的闭包

Web前端TIPS:循环中的闭包,大家来了解下吧。如果你碰到过这种情况:JavaScript代码:var funcs = [];for (var i = 0; i < 3; i++) {  funcs[i] = function() {    console.log("i value is" + i);  };}funcs[0]();funcs[1]();funcs[2]();你预期的输出是:JavaScript代码:i value is 0i value is 1i value is 2而实际的输出却是:JavaScript代码:i value is 3i value is 3i value is 3原因是闭包的捕获机制以及i在内部进行表示。要解决这个问题,你可以这么做:JavaScript代码:var funcs = [];for (var i = 0; i < 3; i++) {  funcs[i] = (function(value) {    console.log("i value is " + i);  })(i);}上述代码有效地复制i的值将其传递给闭包。或者你可以这么做:JavaScript代码:for (let i = 0; i < 3; i++) {  console.log("i value is " + i);}在这里,let将变量作用域限制在for循环中,并在每次迭代中生成一个新的值,因此,我将按照预期的那样在闭包中绑定不同的值。...

了解下2019年国外Web前端开发者薪酬趋势

Web前端开发高端人才紧缺的条件下,高薪挖人习以为常了;今天让我们一起来了解下2019年全球Web前端开发者薪酬,仅供参考。据Techrepublic报道,Web开发专业是2019年最热门的10大技术技能之一。根据美国劳工统计局最新的预测数据,到2026年Web开发岗位的数量将比2016年增长15%:雇主不仅需要Web前端开发者具备用户界面的专业知识,通常也希望他们在HTML、CSS和JavaScript领域拥有扎实的专业基础。而高水平的JS开发者尤其稀缺。JavaScript是雇主最看重的编程语言技能根据技能和薪资分析平台Gooroo的报告,在美国,JavaScript是招聘职位描述中提及频率最高的前五大编程技能之一。Gooroo的报告:每月发布的新职位中提到各类编程语言的数量这意味着什么呢?简单来说,如果你是一名拥有前端开发技能的技术专家,你就不太可能会失业。反过来说如果你是雇主,想聘用一些JavaScript开发人员,那就得有些耐心了——你可能需要等上一段时间才能找到合适的人选。1、关于聘请JS开发人员的建议我面试过很多候选人,其中包括一些高级JavaScript开发人员;我得出的结论是,JavaScript这门语言肯定历史太短了,所以只要找优秀的,愿意学习JS的开发者就行,不用管他们擅长什么技术栈;用不着专门找有过JavaScript开发经验的求职者。2、美国JavaScript开发者平均薪酬Gooroo数据统计2019年JS开发者平均年薪与其他语言的对比然而Gooroo的数据还显示自2019年5月以来美国的JavaScript开发岗位工资大幅下滑了。2019年JavaScript薪酬趋势当然,具体的收入水平因州而异。加利福尼亚州、华盛顿州和弗吉尼亚州的JavaScript开发人员薪酬水平最高,这些州的平均年薪超过109,000美元。但过去一个月中各州提及JS技能的新岗位数量差别很大。其他未超过10万美元大关的州是纽约州、弗吉尼亚州、新泽西州、康涅狄格州、马里兰州等。正如人们猜测的那样,加州的招聘广告是最多的。各州JavaScript开发人员平均年薪和新职位数量(2019年6月)我们还看了一下Indeed的薪水计算器统计出的美国JavaScript程序员平均薪酬:Indeed数据统计各州JavaScript开发人员平均年薪(2019年6月)。根据Indeed的数据,全国范围内JavaScript职位的平均年薪上升到了近111,000美元。至于各州之间的对比,纽约州是最高的,平均年薪约为13万美元。相比之下,康涅狄格州和加利福尼亚州的平均年薪分别为127,411美元和126,497美元,肯塔基州以每年12万美元紧随其后。犹他州排名前五,平均为JavaScript工程师提供近11.6万美元的年薪。截至2019年,Gooroo和Indeed的数据都显示加州和纽约州是JavaScript开发人员收入最高的五大州之二。薪酬与经验时长的关系地理位置是影响开发人员报酬的一个因素,而经验长短则是另一个因素。这里我们转向PayScale,他们就像Indeed一样收集真实用户提交的数据,以调查JavaScript开发者的薪酬与经验长短之间的关系。数据显示,JavaScript开发新人的年薪刚超过6万美元。而拥有超过20年经验的JavaScript开发人员每年可以获得高达113,000美元的收入。按工作时长区分的JS开发者平均年薪JavaScript开发者薪酬与公司规模的关系我们使用PayScale的数据来确定公司规模是否会影响JavaScript开发人员的工资。我们发现答案是肯定的。PayScale的数据:虽然一家拥有至多9名员工的公司可以负担得起每年约73,000美元的JavaScript工程师工资,但拥有超过50,000名员工的庞大企业可以为拥有JavaScript技能的员工提供高达88,000美元的年薪。其他国家JavaScript开发者薪酬美国是公认的开发人员薪酬最高的国家,但编程人才想要获得高收入并不需要局限在美国。例如在瑞士,JavaScript开发人员每年可以赚到近91,000美元。丹麦和挪威的数据分别接近54,000美元和72,000美元,而英国和以色列的JavaScript开发人员平均年薪在59,000美元至65,000美元之间。住在荷兰、芬兰和瑞典的JavaScript工程师通常会要求48,000美元到52,000美元的年薪。2019年JavaScript编程语言统计有很多资料将JavaScript列为2019年最受欢迎的编程语言之一,包括Codingdojo,Businessinsider,Techworld。ITJobsWatch将JavaScript列为需求第二大的编程语言,该结论基于过去三个月发布的招聘广告数量得出。Web技术调查报告显示,截至2019年6月有95.2%的网站使用JavaScript,丝毫不让人惊讶。Web技术调查报告:根据2019年StackOverflow开发人员调查结果,JavaScript连续七年成为最流行的编程语言,有69.7%的专业开发人员使用JavaScript。2019年StackOverflow开发人员调查结果:这个调查还显示React.js、Angular和Vue.js是开发人员最喜爱的Web框架,它们都属于JavaScript库。Hackerrank分享了有关软件开发人员想要在2019年学习的框架的统计数据。Angular和React仍然处于领先地位。总结了解完国外的Web前端开发人员的薪酬,您是否感觉很惊奇,有想说的,可以留言或者加群讨论。...

Google Web前端大神教你如何优化Javascript性能

Web前端大神如何来分析Web前端开发的性能优化。高层级的实用指南这对Web开发人员来说意味着什么?意味着解析(Parse)和编译(Compile)不再像我们曾经想象的那么慢了。所以开发人员在优化Javascript包时,要重点关注以下三大方面:减少下载时间确保Javascript包尽可能地小,特别是对于移动设备。较小的包可以提升下载速度、降低内存使用量,并减少CPU开销。避免只有一个大的Javascript包;如果包大小超过50–100KB,就将其拆分为几个小包。(借助HTTP/2协议的多路复用机制,多个请求和响应消息可以同时传输,从而减少额外请求的开销。)对于移动设备上使用的Javascript包更要尽可能地小,一方面因为网络带宽的制约,另一方面需要要尽量减少内存的使用。缩短执行时间避免持续占用主线程并影响页面响应时间的长时任务,现在脚本下载后的执行时间成为主要的成本开销。避免使用大型内联脚本(因为它们仍然需要在主线程上进行解析和编译)。建议参考一条经验法则:如果一个脚本超过1KB,就不要将其内联(因为当外部脚本大小超过1KB时,就会触发代码缓存)。为什么下载和执行时间很重要?为什么优化下载和执行时间对我们很重要?因为对于低端网络而言,下载时间的影响非常之大。尽管4G(甚至5G)在全球范围内增长迅速,但大多数人的有效连接速度仍然远远低于网络的标称速度。有时当我们外出时,会感觉到网速下降到只有3G的速度(甚至更糟)。JavaScript的执行时间对于CPU较慢的低端手机也非常重要。由于CPU、GPU,和散热限制的不同,高端和低端手机的性能差距巨大。这对JavaScript的性能影响明显,因为它的执行受到CPU性能的制约。事实上,在Chrome之类的浏览器上,JavaScript的执行时间可以达到页面加载总耗时的30%。下图是一个具有典型工作负载的网站(Reddit.com)在一台高端桌面PC上的页面加载情况分析:V8引擎下的Javascript处理时间占整个页面加载时间的10-30%对于移动设备,与高端手机(如Pixel3)相比,在中端手机(如MotoG4)上执行Reddit的Javascript脚本需要3-4倍的耗时,而在低端手机(价格低于100美元的Alcatel1X)上执行Reddit的Javascript脚本更是需要6倍以上的耗时:Reddit的Javascript脚本在几种不同设备(低端、中端和高端)上的执行时间。注意:Reddit对于桌面和移动网络有不同的体验,因此MacBookPro的执行结果无法与其他结果进行比较。当你着手优化JavaScript的执行时间时,你需要留意可能长时间独占界面线程(UIThread)的长时任务。即使页面看起来已经加载完成,这些长时任务也会拖累关键任务的执行。把长时任务分解成较小的任务。通过拆分代码并确定加载顺序,你可以更快地实现页面交互,并有望降低输入延迟。独占主线程的长时任务应该拆分。V8引擎如何提高Javascript解析/编译速度?自Chrome版本60以来,V8引擎的原始JS的解析速度增加了2倍。与此同时,Chrome还做了其他工作一些工作使得解析和编译工作并行化,这使得这部分的成本开销对用户体验的影响变得不是那么显著和关键了。V8引擎通过将解析和编译工作转到worker线程上,使得主线程上的解析和编译工作量平均减少了40%。例如,Facebook降低了46%,Pinterest降低62%,而最大的改进是是YouTube,降低了81%。这是在现有的非主线程流解析/编译性能改进基础上的进一步提升。不同版本的V8引擎的解析时间对比我们还可以图示对比不同Chrome版本的不同V8引擎对CPU处理时间的影响。可以看出,Chrome61解析Facebook的JS脚本所花费的时间,可以供Chrome75解析同样的Facebook的JS脚本,和6个Twitter的JS脚本了。Chrome61解析Facebook的JS脚本所花费的时间,可以供Chrome75解析完成同样的Facebook的JS脚本,和6个Twitter的JS脚本了。让我们深入研究一下这些改进是如何实现的。总的来说,脚本资源可以在worker线程上进行流式解析和编译,这意味着:V8引擎可以在不阻塞主线程的情况下解析和编译JavaScript。当整个HTML解析器遇到<script>标记时,就开始流式处理。遇到阻塞解析器(parse-blocking)的脚本时,HTML解析器就放弃,而对于异步脚本则继续处理。在大多数网络连接速度下,V8引擎的解析速度都比下载速度快,因此在最后一个脚本字节被下载后几毫秒的时间内,V8引擎就能完成解析+编译工作。具体来说,很多老版本的Chrome在开始脚本解析之前,需要将脚本下载完成,这是一种简单的方法,但它没有充分利用CPU的能力。而从版本41到68,Chrome在下载一开始时就立即在单独的线程上解析异步和延迟脚本。JS脚本以多个块下载,V8引擎看到大于30KB的脚本被下载后就会启动脚本流解析工作Chrome71采用了基于任务(task-based)的设置方案。调度器可以一次解析多个异步/延迟脚本,这一改进使得主线程解析时间缩短了约20%,真实网站上的TTI/FID整体提高了大约2%。Chrome71采用了基于任务(task-based)的设置,调度器可以一次解析多个异步/延迟脚本Chrome72开始采用流式处理作为主要的解析方式,现在常规的同步脚本(内联脚本除外)也可以采用这种解析方式。如果主线程需要,我们也可以继续采用基于任务的解析,从而减少不必要地重复工作。旧版的Chrome支持流式解析和编译,其中来自网络的脚本源数据必须先到达Chrome主线程后,再转发给流解析器解析。这通常会导致这样的情况:脚本数据已经从网络上下载完成,但由于主线程上的其他任务(如HTML解析、排版或者JavaScript执行),阻塞了脚本数据的转发,因此流解析器(streamingparser)不得不空等。现在我们正尝试在预加载时开始解析,以前主线程反弹会阻碍这种操作。这些改变如何反映到DevTools中?除上述之外,DevTools中还存在一个问题,它以表明它会独占CPU(完全阻塞)的方式渲染整个解析器任务。但是,不管解析器是否需要数据(数据需要通过主线程)都会阻塞。当我我们从单个流线程转向多个流传输任务时,这个问题变得非常明显。下面是你在Chrome69中看到的情况:DevTools以表明它会独占CPU(完全阻塞)的方式渲染整个解析器任务如上图示,“解析脚本”任务需要1.08秒。但是解析JavaScript其实并没有那么慢!大部分时间除了等待数据通过主线程之外什么都做不了。而在Chrome76中显示的内容就不一样了:在Chrome76中,解析工作被分解为多个较小的流任务。一般来说,DevTools性能窗格非常适合从宏观层面分析你的页面。对于更具体的V8度量指标,如Javascript解析和编译时间,我们建议使用带有运行时调用统计(RCS)的Chrome跟踪工具。在RCS结果中,Parse-Background和Compile-Background会告诉你在主线程外解析和编译Javascript花费了多少时间,而Parse和Compile是针对主线程的度量指标。  这些改变对现实应用的影响是什么?让我们来看一些真实网站的示例,来了解脚本流(scriptstreaming)是如何工作的。主线程和worker线程在MacBookPro上解析和编译Reddit网站的JS所花费的时间对比Reddit.com网站有几个超过100KB的JS包,它们包装在外部函数中,导致在主线程上需要进行大量的延迟编译(lazycompilation)。如上图所示,主线程耗时才是真正关键的,因为主线程持续繁忙会严重影响交互体验。Reddit的大部分时间花在了主线程上,而worker线程或后台线程的使用率很低。可以将一些较大的JS包拆分为几个不需要包装的小包(例如每个包50KB),以最大限度地实现并行化,这样每个包都可以单独进行流解析和编译,并在载入期间减少主线程的解析/编译时间。主线程和worker线程在MacBookPro上解析和编译Facebook网站的JS所花费的时间对比我们再看看像facebook.com这样的网站的情况。Facebook使用了大约292个请求,加载了大约6MB的压缩JS脚本,其中一些是异步的,一些是预加载的,还有一些是低优先级的。它们的许多脚本都非常小,粒度也不大,这有助于后台/workers线程上的整体并行化,因为这些较小的脚本可以同时进行流解析/编译。值得注意地是,像Facebook或Gmail这样老牌的应用程序的桌面版本上有这么多的脚本可能是合理的。但是你的网站可能和Facebook不一样。不管怎样,尽可能地简化你的JS包,不必要的就不要装载了。尽管大多数JavaScript解析和编译工作都可以在后台线程上以流式方式进行,但仍有一些工作必须在主线程上进行。而当主线程繁忙时,页面就无法响应用户输入了。所以要密切关注下载和执行代码对用户体验的影响。注意:目前并不是所有的Javascript引擎和浏览器都实现了脚本流(scriptstreaming)式加载优化。但是我们仍然相信,本文的整体指导会帮助大家全面地提升用户体验。解析JSON的开销JSON语法比JavaScript语法简单很多,所以JSON的解析效率要比Javascript高得多。基于这一点,Web应用程序可以提供类似于JSON的大型配置对象文本,而不是将数据作为Javascript对象文本进行内联,这样可以大大提高Web应用程序的加载性能。如下所示:const data = { foo: 42, bar: 1337 }; // 🐌42, bar: 1337 }; // 🐌……它可以用JSON字符串形式表示,然后在运行时进行JSON解析。如下所示:const data = JSON.parse('{"foo":42,"bar":1337}'); // 🚀JSON.parse('{"foo":42,"bar":1337}'); // 🚀只要JSON字符串只计算一次,那么相比Javascript对象文本,JSON.parse方法就要快得多,冷加载时尤其明显。在为大量数据使用普通对象文本时还有一个额外的风险:它们可能会被解析两次!第一次是文本预解析时。第二次是文本延迟解析时。第一次解析是必须的,可以将对象文本放在顶层或PIFE中来避免第二次解析。重复访问时的解析/编译情况如何?V8引擎的(字节)代码缓存优化可以帮助改善重复访问时的体验。当第一次请求脚本时,Chrome会下载脚本并将其交给V8引擎进行编译。同时将文件存储在浏览器的磁盘缓存中。当第二次请求JS文件时,Chrome会从浏览器缓存中获取该文件,并再次将其交给V8引擎进行编译。然而,这次编译的代码会被序列化,并作为元数据附加到缓存的脚本文件中。V8引擎的代码缓存示意图第三次请求脚本时,Chrome从缓存中获取脚本文件和文件的元数据,并将两者都交给V8引擎。V8引擎会反序列化元数据来跳过编译步骤。如果前两次访问间隔小于72小时内,代码缓存就会启动。如果采用serviceworker来缓存脚本,那么chrome也会主动启动代码缓存。详细信息可以参阅web开发者的代码缓存指南。汇总到了2019年。脚本下载和执行的时间开销已经变成加载脚本的主要瓶颈。所以你应该为你的首屏内容准备一个较小的同步(内联)脚本包,其余部分则使用一个或多个延迟脚本,并且把较大的包拆分成许多小包来按需加载。这样一来就能充分利用V8引擎的并行化能力。在移动设备上,由于网络、内存消耗和CPU执行时间的制约,你需要尽可能地减少脚本的数量,平衡延迟和缓存设置,尽可能地让解析和编译工作在主线程外执行。总结看完后,是不是觉得性能优化又有很多事情要做了呢?就我个人而言,的确如此,感觉之前做的优化都是皮毛而已,您觉得呢?...

为何近几年Web前端“兑变”如此之快?

近几年,前端的发展是大家有目共睹的;设计方面从phtoshop到现在的sketch,前端开发从javascript到jQuery,再到现在的vue,react,webpack等,可以说,这2两年,变化太大。尽管开发人员和技术人员在前端领域中的数量逐年上升,生态系统却有标准化的趋势。新技术和工具的出现正在改变当下的规则。总体趋势肯定会是一种基于组件构成的用户界面标准化,会影响从样式到测试甚至状态管理的所有方面,并且总体具有更好的模块度。这将包括围绕web组件、ES模块、组件焦点工具等技术构建。以下是对未来几年前端开发的一些不完全预测,仅供参考。与框架无关的Web组件这大体上代表了未来。因为这些纯web组件与框架无关,可以在没有框架或任何框架拼写标准化的情况下工作。因为不使用JS语言,并受到很多浏览器的支持。其bundle的大小和消耗也将是最优的,而且VDOM呈现震撼人心。这些组件提供自定义元素,这是一个允许定义新的html标签的Javascript应用程序编程接口,用于指定布局的HTML模板,当然还有本质上特定于组件的影子DOM。在这个领域中需要了解的主要工具是Lit-htmlStencilJS,SvelteJS当然还有Bit,用于可重用的可以在任何地方直接共享、使用和开发的模块组件。当考虑到用户界面开发的未来,以及组件时代中模块度、可重用性、封装性和标准化的原则时,web组件就是答案。框架冲突的未来?现在,在NPM下载中React仍然是前端中的“女王”。我们不会深入探讨“哪个更好,为什么更好”,相反,如果退回一步的话,你会注意到更重要更宏大的部分。围绕组件的前端技术的总体“市场份额”正在增长。新开发人员也在快速涌入,工具的使用空间也越来越大。那么未来5年内哪个框架会成为支配呢?没有人知道。但可以肯定地说,它将是在原生JS生态系统中发挥作用的最佳位置,web组件在其中支配着文档对象模型dom。React在NPM中下载量最高。然而——看看这些数字。似乎在实际web使用中差距非常小。令人震惊吧?实际上,Vue和React在实际使用中很接近。随着未来与框架无关的web组件的标准化,不少人都想知道可能会对用户界面框架冲突产生的影响。事实上,我们都知道React确实不是一个框架。组件分离,重用和构成heBit组件:未来的代码共享、重用和开发。当谈到在不久的将来的前端开发和用户界面组件时,不可能忽视Bit惊人的promise功能。Bit(开放源)分离并将组件(或任何可重用的JS代码)转换为共享的构建块,可供在所有项目和应用中使用和共享。神奇的是——还可以使用Bit从不同项目开发相同组件,同时完全控制源代码更改和整个依赖图。简单地说,通过Bit可以立即在另一个项目中使用一个项目中的组件,并开发和同步更改这两个组件。团队工作时,这个工作流将通过Bit的组件中心bit.dev加强,可以在其中组织和共享团队代码。组件中心提供了在组件上共享和协作所需的一切,从漂亮的搜索和发现体验到实时组件playground,持续集成和持续交付的充分支持等。通过Bit可以充分构建应用程序,即时获得团队和开源社区写入的所有组件,并立即共享新组件或建议对现有组件进行更新。ES模块和内容分发网络ES模块是在浏览器中用模块工作的标准,被ECMAScript标准化。使用ES模块可以很容易地将功能封装进模块中,可以通过内容分发网络等方式使用。随着Firefox60的发布,所有主流的浏览器都将支持ES模块,Nodemteam正致力将ES模块支持添加到Node.js中。另外,用于WebAssembly的ES模块整合将在未来几年内实现。想象一下,JS组件与Bit分离,并通过bit.dev内容分发网络使用。组件级别的状态管理那么状态管理有什么新变化呢?我们只需要在Redux中就能管理一切事务吗?但这可能很难实现充分利用组件,使模块得到重用。React新的ContextAPI和Hooks意味着不需要第三方库,便可以在功能组件级别管理状态,从而提高模块度和可重用性。因此,展望未来,我们可以尝试更多地从封装组件而较少从全球应用商店的角度来考虑状态管理。构成样式化组件Bit模块:由独立逻辑和主题组件构成样式。过去两年有很多关于样式化组件的讨论。从内联层叠样式表或层叠样式表模块到JS中的层叠样式表和样式组件,甚至像stylable这样的中途解决方案,有很多解决方案。未来几年,样式可以作为一种构成。这意味着,组件设计体系应该同时包含逻辑组件和主题组件,可以使用Bit之类的工具构成。通过这种方式可以创建一个设计体系,根据需要进行发展和改变,不会将一个复杂的库强加给不愿意去应用的开发人员。设计工具本身如SketchanFigma,利用will组件来达到这个目的(结合Bi得到最终的组件设计体系。用于数据驱动应用程序的GraphQL应用程序编程接口客户端令人兴奋的是,在客户端有很大的可能性来通过组件使用GraphQL。使用阿波罗可以轻松通过GraphQL构建获取数据的用户界面组件。结合Bit能从正在处理的项目中导入和开发这些组件。通过对应用程序编程接口的智能管理,可以简化围绕数据驱动应用程序开发的工作流,加快开发速度。所以对未来几年的展望绝对是值得的。基于组件的设计工具随着组件变为了设计体系,设计师和开发人员双方都在向彼此靠近。也就是从双方来看,这是可能的。Sketch已经在设计组件之间创建了依赖关系,因此可以模块化的方式设计和更新。代码组件的整合已经出现,这只是时间问题。Figma这类的工具是彻底基于可重用的用户界面元素构建的。FramerTeam正在为编写代码的设计人员构建一个工具,能够在一定程度上控制将用户界面元素转换为可重用的React组件。通过Bit可以将设计的组件转换为可重用的构建块,并可以在任何地方直观发现、使用甚至开发,从而弥补与开发人员之间的差距。Bit+组件设计工具未来大有可为。通过内容分发网络使用Bit和web组件是个完整的构成。总结互联网的高速发展,淘汰一些陈旧的技术和知识是必然;所以作为一名开发者,应该跟上发展的步伐,不断的提高自己,才能不会被淘汰。...

Web前端​开发:从浅入深逐步了解AJAX的来龙去脉

Web前端开发,少不了跟数据(ajax)打交道;对于ajax,必须熟练掌握它的一些细节和基础,然后拓展更深入的技术知识点。本次分享主要为了便于大家逐步认识ajax。首先我们需要了解什么是ajax。什么是ajax?Ajax即“AsynchronousJavascriptAndXML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术。Ajax=异步JavaScript 和XML或者是HTML(标准通用标记语言的子集)。Ajax是一种用于创建快速动态网页的技术。Ajax是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。通过在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。传统的网页(不使用Ajax)如果需要更新内容,必须重载整个网页页面。了解ajax定义后,接下来我们一起来学习下ajax的10种应用。1、使用AJAX的过程可以类比平常我们访问网页过程// 1\. 创建一个 XMLHttpRequest 类型的对象 —— 相当于打开了一个浏览器var xhr = new XMLHttpRequest()    // 2\. 打开与一个网址之间的连接 —— 相当于在地址栏输入访问地址    xhr.open('GET', './time.php')    // 3\. 通过连接发送一次请求 —— 相当于回车或者点击访问发送请求    xhr.send(null)    // 4\. 指定 xhr 状态变化事件处理函数 —— 相当于处理网页呈现后的操作    xhr.onreadystatechange = function () {    // 通过 xhr 的 readyState 判断此次请求的响应是否接收完成    if (this.readyState === 4) {        // 通过 xhr 的 responseText 获取到响应的响应体                console.log(this)    }}2、readyState2、readyState由于readystatechange事件是在xhr对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被触发多次,所以我们有必要了解每一个状态值代表的含义:时间轴:var xhr = new XMLHttpRequest()console.log(xhr.readyState)// => 0// 初始化 请求代理对象xhr.open('GET', 'time.php')console.log(xhr.readyState)// => 1// open 方法已经调用,建立一个与服务端特定端口的连接xhr.send()xhr.addEventListener('readystatechange', function () {    switch (this.readyState) {        case 2:        // => 2        // 已经接受到了响应报文的响应头        // 可以拿到头        // console.log(this.getAllResponseHeaders())        console.log(this.getResponseHeader('server'))        // 但是还没有拿到体        console.log(this.responseText)        break        case 3:        // => 3        // 正在下载响应报文的响应体,有可能响应体为空,也有可能不完整        // 在这里处理响应体不保险(不可靠)        console.log(this.responseText)        break        case 4:        // => 4        // 一切 OK (整个响应报文已经完整下载下来了)        // 这里处理响应体        console.log(this.responseText)        break    }})通过理解每一个状态值的含义得出一个结论:一般我们都是在readyState值为4时,执行响应的后续逻辑。xhr.onreadystatechange = function () {    if (this.readyState === 4) {        // 后续逻辑......    }}3、遵循HTTP本质上XMLHttpRequest就是JavaScript在Web平台中发送HTTP请求的手段,所以我们发送出去的请求任然是HTTP请求,同样符合HTTP约定的格式:// 设置请求报文的请求行xhr.open('GET', './time.php')// 设置请求头xhr.setRequestHeader('Accept', 'text/plain')// 设置请求体xhr.send(null)xhr.onreadystatechange = function () {    if (this.readyState === 4) {        // 获取响应状态码        console.log(this.status)        // 获取响应状态描述        console.log(this.statusText)        // 获取响应头信息        console.log(this.getResponseHeader('Content‐Type')) // 指定响应头        console.log(this.getAllResponseHeader()) // 全部响应头        // 获取响应体        console.log(this.responseText) // 文本形式        console.log(this.responseXML) // XML 形式,了解即可不用了    }}4、GET请求通常在一次GET请求过程中,参数传递都是通过URL地址中的?参数传递。var xhr = new XMLHttpRequest()// GET 请求传递参数通常使用的是问号传参// 这里可以在请求地址后面加上参数,从而传递数据到服务端xhr.open('GET', './delete.php?id=1')// 一般在 GET 请求时无需设置响应体,可以传 null 或者干脆不传xhr.send(null)xhr.onreadystatechange = function () {    if (this.readyState === 4) {        console.log(this.responseText)    }}// 一般情况下 URL 传递的都是参数性质的数据,而 POST 一般都是业务数据5、POST请求POST请求过程中,都是采用请求体承载需要提交的数据。var xhr = new XMLHttpRequest()// open 方法的第一个参数的作用就是设置请求的 methodxhr.open('POST', './add.php')// 设置请求头中的 Content‐Type 为 application/x‐www‐form‐urlencoded// 标识此次请求的请求体格式为 urlencoded 以便于服务端接收数据xhr.setRequestHeader('Content‐Type', 'application/x‐www‐form‐urlencoded')// 需要提交到服务端的数据可以通过 send 方法的参数传递// 格式:key1=value1&key2=value2xhr.send('key1=value1&key2=value2')xhr.onreadystatechange = function () {    if (this.readyState === 4) {        console.log(this.responseText)    }}6、同步与异步HTML复制全屏同步:一个人在同一个时刻只能做一件事情,在执行一些耗时的操作(不需要看管)不去做别的事,只是等待异步:在执行一些耗时的操作(不需要看管)去做别的事,而不是等待xhr.open()方法第三个参数要求传入的是一个bool值,其作用就是设置此次请求是否采用异步方式执行,默认为true,如果需要同步执行可以通过传递false实现:console.log('before ajax')var xhr = new XMLHttpRequest()// 默认第三个参数为 true 意味着采用异步方式执行xhr.open('GET', './time.php', true)xhr.send(null)xhr.onreadystatechange = function () {    if (this.readyState === 4) {        // 这里的代码最后执行        console.log('request done')    }}console.log('after ajax')如果采用同步方式执行,则代码会卡死在xhr.send()这一步:console.log('before ajax')var xhr = new XMLHttpRequest()// 同步方式xhr.open('GET', './time.php', false)// 同步方式 执行需要 先注册事件再调用 send,否则 readystatechange 无法触发xhr.onreadystatechange = function () {    if (this.readyState === 4) {        // 这里的代码最后执行        console.log('request done')    }}xhr.send(null)console.log('after ajax')演示同步异步差异。一定在发送请求send()之前注册readystatechange(不管同步或者异步)为了让这个事件可以更加可靠(一定触发),一定是先注册了解同步模式即可,切记不要使用同步模式。至此,我们已经大致了解了AJAX的基本API。7、响应数据格式提问:如果希望服务端返回一个复杂数据,该如何处理?关心的问题就是服务端发出何种格式的数据,这种格式如何在客户端用JavaScript解析。XML一种数据描述手段,基本现在的项目不用了,淘汰的原因:数据冗余太多。JSON也是一种数据描述手段,类似于JavaScript字面量方式服务端采用JSON格式返回数据,客户端按照JSON格式解析数据。不管是JSON也好,还是XML,只是在AJAX请求过程中用到,并不代表它们之间有必然的联系,它们只是数据协议罢了兼容方案XMLHttpRequest在老版本浏览器(IE5/6)中有兼容问题,可以通过另外一种方式代替var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')8、封装AJAX请求封装函数就可以理解为一个想要做的事情,函数体中约定了这件事情做的过程,直到调用时才开始工作。将函数作为参数传递就像是将一个事情交给别人,这就是委托的概念/*** 发送一个 AJAX 请求* @param {String} method 请求方法* @param {String} url 请求地址* @param {Object} params 请求参数* @param {Function} done 请求完成过后需要做的事情(委托/回调)*/function ajax (method, url, params, done) {        // 统一转换为大写便于后续判断        method = method.toUpperCase()        // 对象形式的参数转换为 urlencoded 格式        var pairs = []        for (var key in params) {            pairs.push(key + '=' + params[key])        }    var querystring = pairs.join('&')    var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new    ActiveXObject('Microsoft.XMLHTTP')    xhr.addEventListener('readystatechange', function () {        if (this.readyState !== 4) return        // 尝试通过 JSON 格式解析响应体        try {            done(JSON.parse(this.responseText))        } catch (e) {            done(this.responseText)        }    })    // 如果是 GET 请求就设置 URL 地址 问号参数    if (method === 'GET') {        url += '?' + querystring    }    xhr.open(method, url)    // 如果是 POST 请求就设置请求体    var data = null    if (method === 'POST') {        xhr.setRequestHeader('Content‐Type', 'application/x‐www‐form‐urlencoded')        data = querystring    }    xhr.send(data)}ajax('get', './get.php', { id: 123 }, function (data) {    console.log(data)})ajax('post', './post.php', { foo: 'posted data' }, function (data) {    console.log(data)})9、jQuery中的AJAX$.ajax({    url: './get.php',    type: 'get',    dataType: 'json',    data: { id: 1 },    beforeSend: function (xhr) {        console.log('before send')    },    success: function (data) {        console.log(data)    },    error: function (err) {        console.log(err)    },    complete: function () {        console.log('request completed')    }})常用选项参数介绍:url:请求地址type:请求方法,默认为 getdataType:服务端响应数据类型ajax('get', './get.php', { id: 123 }, function (data) {    console.log(data)})ajax('post', './post.php', { foo: 'posted data' }, function (data) {    console.log(data)})$.ajax({    url: './get.php',    type: 'get',    dataType: 'json',    data: { id: 1 },    beforeSend: function (xhr) {        console.log('before send')    },    success: function (data) {        console.log(data)    },    error: function (err) {        console.log(err)    },    complete: function () {        console.log('request completed')    }})常用选项参数介绍:url:请求地址type:请求方法,默认为getdataType:服务端响应数据类型contentType:请求体内容类型,默认application/x-www-form-urlencodeddata:需要传递到服务端的数据,如果GET则通过URL传递,如果POST则通过请求体传递timeout:请求超时时间beforeSend:请求发起之前触发success:请求成功之后触发(响应状态码200)error:请求失败触发complete:请求完成触发(不管成功与否)10、跨域相关概念同源策略是浏览器的一种安全策略,所谓同源是指域名,协议,端口完全相同,只有同源的地址才可以相互通过AJAX的方式请求。同源或者不同源说的是两个地址之间的关系,不同源地址之间请求我们称之为跨域请求什么是同源?例如:http://www.example.com/detail...与一下地址对比JSONPJSONwithPadding,是一种借助于script标签发送跨域请求的技巧。其原理就是在客户端借助script标签请求服务端的一个动态网页(php文件),服务端的这个动态网页返回一段带有函数调用的JavaScript全局函数调用的脚本,将原本需要返回给客户端的数据传递进去。以后绝大多数情况都是采用JSONP的手段完成不同源地址之间的跨域请求<script src="http://api.zce.me/users.php?callback=foo"></script>foo(['我', '是', '你', '原', '本', '需', '要', '的', '数', '据'])总结由于XMLHttpRequest无法发送不同源地址之间的跨域请求,所以我们必须要另寻他法,script这种方案就是我们最终选择的方式,我们把这种方式称之为JSONP,如果你不了解原理,先记住怎么用,多用一段时间再来看原理。...

Web前端开发笔记:闭包

闭包是一个重要的JavaScript模式,可以私有访问变量。在本例中,createGreeter返回一个匿名函数,这个函数可以访问参数greeting(在这里是“Hello”)。在后续的调用中,sayHello将有权访问这个greeting!function createGreeter(greeting) {  return function(name) {    console.log(greeting + ', ' + name);  }}const sayHello = createGreeter('Hello');sayHello('Joe');// Hello, Joe在更真实的场景中,你可以设想一个初始函数apiConnect(apiKey),它返回一些使用APIkey的方法。在这种情况下,apiKey只需要提供一次即可。function apiConnect(apiKey) {  function get(route) {    return fetch(`${route}?key=${apiKey}`);  }  function post(route, params) {    return fetch(route, {      method: 'POST',      body: JSON.stringify(params),        headers: {          'Authorization': `Bearer ${apiKey}`        }      })  }  return { get, post }}const api = apiConnect('my-secret-key');// No need to include the apiKey anymoreapi.get('http://www.example.com/get-endpoint');api.post('http://www.example.com/post-endpoint', { name: 'Joe' });...

Web前端面试题:如何进行seo优化

Web前端面试题:如何进行seo优化。如下:1、合理的title、description、keywords:搜索对着三项的权重逐个减小,title值强调重点即可;description把页面内容高度概括,不可过分堆砌关键词;keywords列举出重要关键词。2、语义化的HTML代码,符合W3C规范:语义化代码让搜索引擎容易理解网页3、重要内容HTML代码放在最前:搜索引擎抓取HTML顺序是从上到下,保证重要内容一定会被抓取4、重要内容不要用js输出:爬虫不会执行js获取内容5、少用iframe:搜索引擎不会抓取iframe中的内容6、非装饰性图片必须加alt7、提高网站速度:网站速度是搜索引擎排序的一个重要指标。这里可以补充下,网页性能优化的文章,请点击:性能优化汇总专题前后端分离的项目如何seo1、使用prerender。但是回答prerender,面试官肯定会问你,如果不用prerender,让你直接去实现,好的,请看下面的第二个答案。2、先去www.baidu.com/robots.txt找出常见的爬虫,然后在nginx上判断来访问页面用户的User-Agent是否是爬虫,如果是爬虫,就用nginx方向代理到我们自己用nodejs+puppeteer实现的爬虫服务器上,然后用你的爬虫服务器爬自己的前后端分离的前端项目页面,增加扒页面的接收延时,保证异步渲染的接口数据返回,最后得到了页面的数据,返还给来访问的爬虫即可。...

每日一学每天一小步 成功一大步!

最新留言

  • sasa

    谢谢,刚好遇到,解决了。...

  • steven

    现在不是都在搞windows11了吗?...

  • 2sdf

    不错啊...

  • 啊啊啊

    你给我低调点好吧...

  • 啊啊啊

    靠北啊...

  • 啊啊啊

    方法描述concat()连接两个或更多的数组,并返回结果。join()把数组的所有元素放入...

  • 访客

    阿斯顿...

首页|JavaScript|HTML|HTML4|HTML5|CSS3|开发工具|性能优化|移动开发|前端教程|性能优化|开发工具|酷站欣赏|UI设计|前端教程

Copyright © 2022 Web前端之家(www.jiangweishan.com) 版权所有 All Rights Reserved.
粤ICP备12067512号-1

Copyright Your WebSite.Some Rights Reserved.

Powered By Z-BlogPHP 1.7.2